QmlDesigner: Add preset layout options for 3D View

Task-number: QDS-14907
Change-Id: I0937a64dc1820727178372663e2913f2c2239f10
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Amr Essam
2025-03-31 14:31:04 +03:00
committed by Amr Elsayed
parent d44cbdd689
commit 4c7e6c6992
14 changed files with 364 additions and 229 deletions

View File

@@ -16,7 +16,7 @@ public:
Edit3DToolState, Edit3DToolState,
Render3DView, Render3DView,
ActiveSceneChanged, ActiveSceneChanged,
ActiveSplitChanged, ActiveViewportChanged,
RenderModelNodePreviewImage, RenderModelNodePreviewImage,
Import3DPreviewIcon, Import3DPreviewIcon,
Import3DPreviewImage, Import3DPreviewImage,

View File

@@ -51,10 +51,11 @@ enum class View3DActionType {
ParticlesRestart, ParticlesRestart,
ParticlesSeek, ParticlesSeek,
SyncEnvBackground, SyncEnvBackground,
ViewportPreset,
GetNodeAtPos, GetNodeAtPos,
GetNodeAtMainScenePos, GetNodeAtMainScenePos,
SetBakeLightsView3D, SetBakeLightsView3D,
SplitViewToggle, ViewportViewToggle,
MaterialOverride, MaterialOverride,
ShowWireframe, ShowWireframe,
FlyModeToggle, FlyModeToggle,

View File

@@ -98,11 +98,11 @@ void Edit3DCanvas::setFlyMode(bool enabled, const QPoint &pos)
m_flyModeStartCursorPos = pos; m_flyModeStartCursorPos = pos;
m_flyModeFirstUpdate = true; m_flyModeFirstUpdate = true;
// Hide cursor on the middle of the active split to make the wheel work during flight mode. // Hide cursor on the middle of the active viewport to make the wheel work during flight mode.
// We can't rely on current activeSplit value, as mouse press to enter flight mode can change the // We can't rely on current activeViewport value, as mouse press to enter flight mode can change the
// active split, so hide the cursor based on its current location. // active viewport, so hide the cursor based on its current location.
QPoint center = mapToGlobal(QPoint(width() / 2, height() / 2)); QPoint center = mapToGlobal(QPoint(width() / 2, height() / 2));
if (m_parent->view()->isSplitView()) { if (m_parent->view()->isMultiViewportView()) {
if (pos.x() <= center.x()) { if (pos.x() <= center.x()) {
if (pos.y() <= center.y()) if (pos.y() <= center.y())
m_hiddenCursorPos = mapToGlobal(QPoint(width() / 4, height() / 4)); m_hiddenCursorPos = mapToGlobal(QPoint(width() / 4, height() / 4));
@@ -192,7 +192,7 @@ void Edit3DCanvas::mouseMoveEvent(QMouseEvent *e)
if (!m_flyModeFirstUpdate) { if (!m_flyModeFirstUpdate) {
// We notify explicit camera rotation needs for QML Puppet rather than relying on mouse events, // We notify explicit camera rotation needs for QML Puppet rather than relying on mouse events,
// as mouse isn't grabbed on QML Puppet side and can't handle fast movements that go out of // as mouse isn't grabbed on QML Puppet side and can't handle fast movements that go out of
// edit camera mouse area. This also simplifies split view handling. // edit camera mouse area. This also simplifies viewport view handling.
QPointF diff = m_isQDSTrusted ? (m_hiddenCursorPos - globalPos) QPointF diff = m_isQDSTrusted ? (m_hiddenCursorPos - globalPos)
: (m_lastCursorPos - e->globalPosition().toPoint()); : (m_lastCursorPos - e->globalPosition().toPoint());

View File

@@ -73,7 +73,7 @@ Edit3DView::Edit3DView(ExternalDependenciesInterface &externalDependencies)
connect(&m_compressionTimer, &QTimer::timeout, this, &Edit3DView::handleEntriesChanged); connect(&m_compressionTimer, &QTimer::timeout, this, &Edit3DView::handleEntriesChanged);
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
m_splitToolStates.append({0, false, i == 0}); m_viewportToolStates.append({0, false, i == 0});
} }
void Edit3DView::createEdit3DWidget() void Edit3DView::createEdit3DWidget()
@@ -120,15 +120,15 @@ void Edit3DView::renderImage3DChanged(const QImage &img)
void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState) void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
{ {
const QString activeSplitKey = QStringLiteral("activeSplit"); const QString activeViewportKey = QStringLiteral("activeViewport");
if (sceneState.contains(activeSplitKey)) { if (sceneState.contains(activeViewportKey)) {
setActiveSplit(sceneState[activeSplitKey].toInt()); setActiveViewport(sceneState[activeViewportKey].toInt());
// If the sceneState contained just activeSplit key, then this is simply an active split // If the sceneState contained just activeViewport key, then this is simply an active Viewport
// change rather than entire active scene change, and we don't need to process further. // change rather than entire active scene change, and we don't need to process further.
if (sceneState.size() == 1) if (sceneState.size() == 1)
return; return;
} else { } else {
setActiveSplit(0); setActiveViewport(0);
} }
const QString sceneKey = QStringLiteral("sceneInstanceId"); const QString sceneKey = QStringLiteral("sceneInstanceId");
@@ -146,7 +146,7 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
const QString particleEmitterKey = QStringLiteral("showParticleEmitter"); const QString particleEmitterKey = QStringLiteral("showParticleEmitter");
const QString particlesPlayKey = QStringLiteral("particlePlay"); const QString particlesPlayKey = QStringLiteral("particlePlay");
const QString syncEnvBgKey = QStringLiteral("syncEnvBackground"); const QString syncEnvBgKey = QStringLiteral("syncEnvBackground");
const QString splitViewKey = QStringLiteral("splitView"); const QString activePresetKey = QStringLiteral("activePreset");
const QString matOverrideKey = QStringLiteral("matOverride"); const QString matOverrideKey = QStringLiteral("matOverride");
const QString showWireframeKey = QStringLiteral("showWireframe"); const QString showWireframeKey = QStringLiteral("showWireframe");
@@ -177,10 +177,10 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
if (sceneState.contains(perspectiveKey)) { if (sceneState.contains(perspectiveKey)) {
const QVariantList showList = sceneState[perspectiveKey].toList(); const QVariantList showList = sceneState[perspectiveKey].toList();
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
m_splitToolStates[i].isPerspective = i < showList.size() ? showList[i].toBool() : i == 0; m_viewportToolStates[i].isPerspective = i < showList.size() ? showList[i].toBool() : i == 0;
} else { } else {
for (int i = 0; i < 4; ++i) { for (int i = 0; i < 4; ++i) {
SplitToolState &state = m_splitToolStates[i]; ViewportToolState &state = m_viewportToolStates[i];
state.isPerspective = i == 0; state.isPerspective = i == 0;
} }
} }
@@ -235,26 +235,21 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
else else
m_particlesPlayAction->action()->setChecked(true); m_particlesPlayAction->action()->setChecked(true);
if (sceneState.contains(splitViewKey))
m_splitViewAction->action()->setChecked(sceneState[splitViewKey].toBool());
else
m_splitViewAction->action()->setChecked(false);
if (sceneState.contains(matOverrideKey)) { if (sceneState.contains(matOverrideKey)) {
const QVariantList overrides = sceneState[matOverrideKey].toList(); const QVariantList overrides = sceneState[matOverrideKey].toList();
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
m_splitToolStates[i].matOverride = i < overrides.size() ? overrides[i].toInt() : 0; m_viewportToolStates[i].matOverride = i < overrides.size() ? overrides[i].toInt() : 0;
} else { } else {
for (SplitToolState &state : m_splitToolStates) for (ViewportToolState &state : m_viewportToolStates)
state.matOverride = 0; state.matOverride = 0;
} }
if (sceneState.contains(showWireframeKey)) { if (sceneState.contains(showWireframeKey)) {
const QVariantList showList = sceneState[showWireframeKey].toList(); const QVariantList showList = sceneState[showWireframeKey].toList();
for (int i = 0; i < 4; ++i) for (int i = 0; i < 4; ++i)
m_splitToolStates[i].showWireframe = i < showList.size() ? showList[i].toBool() : false; m_viewportToolStates[i].showWireframe = i < showList.size() ? showList[i].toBool() : false;
} else { } else {
for (SplitToolState &state : m_splitToolStates) for (ViewportToolState &state : m_viewportToolStates)
state.showWireframe = false; state.showWireframe = false;
} }
@@ -274,10 +269,10 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
storeCurrentSceneEnvironment(); storeCurrentSceneEnvironment();
} }
void Edit3DView::setActiveSplit(int split) void Edit3DView::setActiveViewport(int viewport)
{ {
m_activeSplit = split; m_activeViewport = viewport;
m_cameraModeAction->action()->setChecked(m_splitToolStates[m_activeSplit].isPerspective); m_cameraModeAction->action()->setChecked(m_viewportToolStates[m_activeViewport].isPerspective);
} }
void Edit3DView::modelAttached(Model *model) void Edit3DView::modelAttached(Model *model)
@@ -709,6 +704,45 @@ void Edit3DView::createSyncEnvBackgroundAction()
tooltip); tooltip);
} }
void Edit3DView::createViewportPresetActions()
{
auto createViewportPresetAction = [this](std::unique_ptr<Edit3DAction> &targetAction,
const QByteArray &id,
const QString &label,
bool isChecked) {
auto operation = [this, &targetAction, label](const SelectionContext &) {
for (Edit3DAction *action : std::as_const(m_viewportPresetActions)) {
if (action->menuId() != targetAction->menuId())
action->action()->setChecked(false);
}
emitView3DAction(View3DActionType::ViewportPreset, label);
};
targetAction = std::make_unique<Edit3DAction>(
id,
View3DActionType::Empty,
label,
QKeySequence(),
true,
isChecked,
QIcon(),
this,
operation);
};
createViewportPresetAction(m_viewportPresetSingleAction, Constants::EDIT3D_PRESET_SINGLE, "Single", true);
createViewportPresetAction(m_viewportPresetQuadAction, Constants::EDIT3D_PRESET_QUAD, "Quad", false);
createViewportPresetAction(m_viewportPreset3Left1RightAction, Constants::EDIT3D_PRESET_3LEFT1RIGHT, "3Left1Right", false);
createViewportPresetAction(m_viewportPreset2HorizontalAction, Constants::EDIT3D_PRESET_2HORIZONTAL, "2Horizontal", false);
createViewportPresetAction(m_viewportPreset2VerticalAction, Constants::EDIT3D_PRESET_2VERTICAL, "2Vertical", false);
m_viewportPresetActions << m_viewportPresetSingleAction.get();
m_viewportPresetActions << m_viewportPresetQuadAction.get();
m_viewportPresetActions << m_viewportPreset3Left1RightAction.get();
m_viewportPresetActions << m_viewportPreset2HorizontalAction.get();
m_viewportPresetActions << m_viewportPreset2VerticalAction.get();
}
void Edit3DView::createSeekerSliderAction() void Edit3DView::createSeekerSliderAction()
{ {
m_seekerAction = std::make_unique<Edit3DParticleSeekerAction>( m_seekerAction = std::make_unique<Edit3DParticleSeekerAction>(
@@ -930,27 +964,27 @@ void Edit3DView::storeCurrentSceneEnvironment()
} }
} }
const QList<Edit3DView::SplitToolState> &Edit3DView::splitToolStates() const const QList<Edit3DView::ViewportToolState> &Edit3DView::viewportToolStates() const
{ {
return m_splitToolStates; return m_viewportToolStates;
} }
void Edit3DView::setSplitToolState(int splitIndex, const SplitToolState &state) void Edit3DView::setViewportToolState(int viewportIndex, const ViewportToolState &state)
{ {
if (splitIndex >= m_splitToolStates.size()) if (viewportIndex >= m_viewportToolStates.size())
return; return;
m_splitToolStates[splitIndex] = state; m_viewportToolStates[viewportIndex] = state;
} }
int Edit3DView::activeSplit() const int Edit3DView::activeViewport() const
{ {
return m_activeSplit; return m_activeViewport;
} }
bool Edit3DView::isSplitView() const bool Edit3DView::isMultiViewportView() const
{ {
return m_splitViewAction->action()->isChecked(); return m_viewportPresetsMenuAction->action()->isChecked();
} }
void Edit3DView::createEdit3DActions() void Edit3DView::createEdit3DActions()
@@ -1022,12 +1056,12 @@ void Edit3DView::createEdit3DActions()
SelectionContextOperation cameraModeTrigger = [this](const SelectionContext &) { SelectionContextOperation cameraModeTrigger = [this](const SelectionContext &) {
QVariantList list; QVariantList list;
for (int i = 0; i < m_splitToolStates.size(); ++i) { for (int i = 0; i < m_viewportToolStates.size(); ++i) {
Edit3DView::SplitToolState state = m_splitToolStates[i]; Edit3DView::ViewportToolState state = m_viewportToolStates[i];
if (i == m_activeSplit) { if (i == m_activeViewport) {
bool isChecked = m_cameraModeAction->action()->isChecked(); bool isChecked = m_cameraModeAction->action()->isChecked();
state.isPerspective = isChecked; state.isPerspective = isChecked;
setSplitToolState(i, state); setViewportToolState(i, state);
list.append(isChecked); list.append(isChecked);
} else { } else {
list.append(state.isPerspective); list.append(state.isPerspective);
@@ -1315,14 +1349,24 @@ void Edit3DView::createEdit3DActions()
this, this,
snapConfigTrigger); snapConfigTrigger);
m_splitViewAction = std::make_unique<Edit3DAction>(QmlDesigner::Constants::EDIT3D_SPLIT_VIEW, SelectionContextOperation viewportPresetsActionTrigger = [this](const SelectionContext &) {
View3DActionType::SplitViewToggle, if (!edit3DWidget()->viewportPresetsMenu())
Tr::tr("Toggle Split View On/Off"), return;
QKeySequence(Qt::CTRL | Qt::ALT | Qt::Key_Q),
true, edit3DWidget()->showViewportPresetsMenu(
!edit3DWidget()->viewportPresetsMenu()->isVisible(),
resolveToolbarPopupPos(m_viewportPresetsMenuAction.get()));
};
m_viewportPresetsMenuAction = std::make_unique<Edit3DAction>(QmlDesigner::Constants::EDIT3D_PRESETS,
View3DActionType::Empty,
Tr::tr("Show Viewport Modes"),
QKeySequence(),
false,
false, false,
toolbarIcon(DesignerIcons::SplitViewIcon), toolbarIcon(DesignerIcons::SplitViewIcon),
this); this,
viewportPresetsActionTrigger);
SelectionContextOperation cameraSpeedConfigTrigger = [this](const SelectionContext &) { SelectionContextOperation cameraSpeedConfigTrigger = [this](const SelectionContext &) {
if (!m_cameraSpeedConfiguration) { if (!m_cameraSpeedConfiguration) {
@@ -1372,7 +1416,7 @@ void Edit3DView::createEdit3DActions()
m_leftActions << nullptr; m_leftActions << nullptr;
m_leftActions << m_visibilityTogglesAction.get(); m_leftActions << m_visibilityTogglesAction.get();
m_leftActions << m_backgroundColorMenuAction.get(); m_leftActions << m_backgroundColorMenuAction.get();
m_leftActions << m_splitViewAction.get(); m_leftActions << m_viewportPresetsMenuAction.get();
m_rightActions << m_particleViewModeAction.get(); m_rightActions << m_particleViewModeAction.get();
m_rightActions << m_particlesPlayAction.get(); m_rightActions << m_particlesPlayAction.get();
@@ -1400,6 +1444,8 @@ void Edit3DView::createEdit3DActions()
m_backgroundColorActions << m_selectGridColorAction.get(); m_backgroundColorActions << m_selectGridColorAction.get();
m_backgroundColorActions << m_syncEnvBackgroundAction.get(); m_backgroundColorActions << m_syncEnvBackgroundAction.get();
m_backgroundColorActions << m_resetColorAction.get(); m_backgroundColorActions << m_resetColorAction.get();
createViewportPresetActions();
} }
QVector<Edit3DAction *> Edit3DView::leftActions() const QVector<Edit3DAction *> Edit3DView::leftActions() const
@@ -1422,6 +1468,10 @@ QVector<Edit3DAction *> Edit3DView::backgroundColorActions() const
return m_backgroundColorActions; return m_backgroundColorActions;
} }
QVector<Edit3DAction *> Edit3DView::viewportPresetActions() const
{
return m_viewportPresetActions;
}
Edit3DAction *Edit3DView::edit3DAction(View3DActionType type) const Edit3DAction *Edit3DView::edit3DAction(View3DActionType type) const
{ {

View File

@@ -39,7 +39,7 @@ class QMLDESIGNERCOMPONENTS_EXPORT Edit3DView : public AbstractView
Q_OBJECT Q_OBJECT
public: public:
struct SplitToolState struct ViewportToolState
{ {
int matOverride = 0; int matOverride = 0;
bool showWireframe = false; bool showWireframe = false;
@@ -92,6 +92,7 @@ public:
QVector<Edit3DAction *> rightActions() const; QVector<Edit3DAction *> rightActions() const;
QVector<Edit3DAction *> visibilityToggleActions() const; QVector<Edit3DAction *> visibilityToggleActions() const;
QVector<Edit3DAction *> backgroundColorActions() const; QVector<Edit3DAction *> backgroundColorActions() const;
QVector<Edit3DAction *> viewportPresetActions() const;
Edit3DAction *edit3DAction(View3DActionType type) const; Edit3DAction *edit3DAction(View3DActionType type) const;
Edit3DBakeLightsAction *bakeLightsAction() const; Edit3DBakeLightsAction *bakeLightsAction() const;
@@ -110,11 +111,11 @@ public:
void setCameraSpeedAuxData(double speed, double multiplier); void setCameraSpeedAuxData(double speed, double multiplier);
void getCameraSpeedAuxData(double &speed, double &multiplier); void getCameraSpeedAuxData(double &speed, double &multiplier);
const QList<SplitToolState> &splitToolStates() const; const QList<ViewportToolState> &viewportToolStates() const;
void setSplitToolState(int splitIndex, const SplitToolState &state); void setViewportToolState(int viewportIndex, const ViewportToolState &state);
int activeSplit() const; int activeViewport() const;
bool isSplitView() const; bool isMultiViewportView() const;
void setFlyMode(bool enabled); void setFlyMode(bool enabled);
void emitView3DAction(View3DActionType type, const QVariant &value); void emitView3DAction(View3DActionType type, const QVariant &value);
@@ -147,12 +148,13 @@ private:
void createGridColorSelectionAction(); void createGridColorSelectionAction();
void createResetColorAction(QAction *syncEnvBackgroundAction); void createResetColorAction(QAction *syncEnvBackgroundAction);
void createSyncEnvBackgroundAction(); void createSyncEnvBackgroundAction();
void createViewportPresetActions();
void createSeekerSliderAction(); void createSeekerSliderAction();
void syncCameraSpeedToNewView(); void syncCameraSpeedToNewView();
QmlObjectNode currentSceneEnv(); QmlObjectNode currentSceneEnv();
void storeCurrentSceneEnvironment(); void storeCurrentSceneEnvironment();
void setActiveSplit(int split); void setActiveViewport(int viewportIndex);
QPoint resolveToolbarPopupPos(Edit3DAction *action) const; QPoint resolveToolbarPopupPos(Edit3DAction *action) const;
@@ -164,6 +166,7 @@ private:
QVector<Edit3DAction *> m_rightActions; QVector<Edit3DAction *> m_rightActions;
QVector<Edit3DAction *> m_visibilityToggleActions; QVector<Edit3DAction *> m_visibilityToggleActions;
QVector<Edit3DAction *> m_backgroundColorActions; QVector<Edit3DAction *> m_backgroundColorActions;
QVector<Edit3DAction *> m_viewportPresetActions;
QMap<View3DActionType, Edit3DAction *> m_edit3DActions; QMap<View3DActionType, Edit3DAction *> m_edit3DActions;
std::unique_ptr<Edit3DAction> m_selectionModeAction; std::unique_ptr<Edit3DAction> m_selectionModeAction;
@@ -191,7 +194,14 @@ private:
std::unique_ptr<Edit3DAction> m_selectBackgroundColorAction; std::unique_ptr<Edit3DAction> m_selectBackgroundColorAction;
std::unique_ptr<Edit3DAction> m_selectGridColorAction; std::unique_ptr<Edit3DAction> m_selectGridColorAction;
std::unique_ptr<Edit3DAction> m_resetColorAction; std::unique_ptr<Edit3DAction> m_resetColorAction;
std::unique_ptr<Edit3DAction> m_splitViewAction;
// Viewport presets actions
std::unique_ptr<Edit3DAction> m_viewportPresetSingleAction;
std::unique_ptr<Edit3DAction> m_viewportPresetQuadAction;
std::unique_ptr<Edit3DAction> m_viewportPreset3Left1RightAction;
std::unique_ptr<Edit3DAction> m_viewportPreset2HorizontalAction;
std::unique_ptr<Edit3DAction> m_viewportPreset2VerticalAction;
std::unique_ptr<Edit3DAction> m_viewportPresetsMenuAction;
// View3DActionType::Empty actions // View3DActionType::Empty actions
std::unique_ptr<Edit3DAction> m_resetAction; std::unique_ptr<Edit3DAction> m_resetAction;
@@ -216,9 +226,9 @@ private:
bool m_isBakingLightsSupported = false; bool m_isBakingLightsSupported = false;
QPointer<SnapConfiguration> m_snapConfiguration; QPointer<SnapConfiguration> m_snapConfiguration;
QPointer<CameraSpeedConfiguration> m_cameraSpeedConfiguration; QPointer<CameraSpeedConfiguration> m_cameraSpeedConfiguration;
int m_activeSplit = 0; int m_activeViewport = 0;
QList<SplitToolState> m_splitToolStates; QList<ViewportToolState> m_viewportToolStates;
ModelNode m_contextMenuPendingNode; ModelNode m_contextMenuPendingNode;
ModelNode m_pickView3dNode; ModelNode m_pickView3dNode;

View File

@@ -179,6 +179,10 @@ Edit3DWidget::Edit3DWidget(Edit3DView *view)
handleActions(view->backgroundColorActions(), m_backgroundColorMenu, false); handleActions(view->backgroundColorActions(), m_backgroundColorMenu, false);
m_viewportPresetsMenu = new QMenu(this);
m_viewportPresetsMenu->setToolTipsVisible(true);
handleActions(view->viewportPresetActions(), m_viewportPresetsMenu, false);
createContextMenu(); createContextMenu();
// Onboarding label contains instructions for new users how to get 3D content into the project // Onboarding label contains instructions for new users how to get 3D content into the project
@@ -568,11 +572,11 @@ void Edit3DWidget::onMatOverrideAction(QAction *action)
return; return;
QVariantList list; QVariantList list;
for (int i = 0; i < m_view->splitToolStates().size(); ++i) { for (int i = 0; i < m_view->viewportToolStates().size(); ++i) {
Edit3DView::SplitToolState state = m_view->splitToolStates()[i]; Edit3DView::ViewportToolState state = m_view->viewportToolStates()[i];
if (i == m_view->activeSplit()) { if (i == m_view->activeViewport()) {
state.matOverride = action->data().toInt(); state.matOverride = action->data().toInt();
m_view->setSplitToolState(i, state); m_view->setViewportToolState(i, state);
list.append(action->data()); list.append(action->data());
} else { } else {
list.append(state.matOverride); list.append(state.matOverride);
@@ -588,11 +592,11 @@ void Edit3DWidget::onWireframeAction()
return; return;
QVariantList list; QVariantList list;
for (int i = 0; i < m_view->splitToolStates().size(); ++i) { for (int i = 0; i < m_view->viewportToolStates().size(); ++i) {
Edit3DView::SplitToolState state = m_view->splitToolStates()[i]; Edit3DView::ViewportToolState state = m_view->viewportToolStates()[i];
if (i == m_view->activeSplit()) { if (i == m_view->activeViewport()) {
state.showWireframe = m_wireFrameAction->isChecked(); state.showWireframe = m_wireFrameAction->isChecked();
m_view->setSplitToolState(i, state); m_view->setViewportToolState(i, state);
list.append(m_wireFrameAction->isChecked()); list.append(m_wireFrameAction->isChecked());
} else { } else {
list.append(state.showWireframe); list.append(state.showWireframe);
@@ -610,11 +614,11 @@ void Edit3DWidget::onResetAllOverridesAction()
QVariantList wList; QVariantList wList;
QVariantList mList; QVariantList mList;
for (int i = 0; i < m_view->splitToolStates().size(); ++i) { for (int i = 0; i < m_view->viewportToolStates().size(); ++i) {
Edit3DView::SplitToolState state; Edit3DView::ViewportToolState state;
state.showWireframe = false; state.showWireframe = false;
state.matOverride = 0; state.matOverride = 0;
m_view->setSplitToolState(i, state); m_view->setViewportToolState(i, state);
wList.append(state.showWireframe); wList.append(state.showWireframe);
mList.append(state.matOverride); mList.append(state.matOverride);
} }
@@ -675,6 +679,21 @@ void Edit3DWidget::showBackgroundColorMenu(bool show, const QPoint &pos)
m_backgroundColorMenu->close(); m_backgroundColorMenu->close();
} }
QMenu *Edit3DWidget::viewportPresetsMenu() const
{
return m_viewportPresetsMenu.data();
}
void Edit3DWidget::showViewportPresetsMenu(bool show, const QPoint &pos)
{
if (m_viewportPresetsMenu.isNull())
return;
if (show)
m_viewportPresetsMenu->popup(pos);
else
m_viewportPresetsMenu->close();
}
void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d) void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d)
{ {
auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils(); auto compUtils = QmlDesignerPlugin::instance()->documentManager().generatedComponentUtils();
@@ -711,11 +730,11 @@ void Edit3DWidget::showContextMenu(const QPoint &pos, const ModelNode &modelNode
m_materialsAction->updateMenu(view()->selectedModelNodes()); m_materialsAction->updateMenu(view()->selectedModelNodes());
if (m_view) { if (m_view) {
int idx = m_view->activeSplit(); int idx = m_view->activeViewport();
m_wireFrameAction->setChecked(m_view->splitToolStates()[idx].showWireframe); m_wireFrameAction->setChecked(m_view->viewportToolStates()[idx].showWireframe);
for (QAction *a : std::as_const(m_matOverrideActions)) for (QAction *a : std::as_const(m_matOverrideActions))
a->setChecked(false); a->setChecked(false);
int type = m_view->splitToolStates()[idx].matOverride; int type = m_view->viewportToolStates()[idx].matOverride;
m_matOverrideActions[type]->setChecked(true); m_matOverrideActions[type]->setChecked(true);
} }

View File

@@ -58,6 +58,9 @@ public:
QMenu *backgroundColorMenu() const; QMenu *backgroundColorMenu() const;
void showBackgroundColorMenu(bool show, const QPoint &pos); void showBackgroundColorMenu(bool show, const QPoint &pos);
QMenu *viewportPresetsMenu() const;
void showViewportPresetsMenu(bool show, const QPoint &pos);
void showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d); void showContextMenu(const QPoint &pos, const ModelNode &modelNode, const QVector3D &pos3d);
void updateCreateSubMenu(const QList<ItemLibraryDetails> &entriesList); void updateCreateSubMenu(const QList<ItemLibraryDetails> &entriesList);
@@ -91,6 +94,7 @@ private:
Core::IContext *m_context = nullptr; Core::IContext *m_context = nullptr;
QPointer<QMenu> m_visibilityTogglesMenu; QPointer<QMenu> m_visibilityTogglesMenu;
QPointer<QMenu> m_backgroundColorMenu; QPointer<QMenu> m_backgroundColorMenu;
QPointer<QMenu> m_viewportPresetsMenu;
QPointer<QMenu> m_contextMenu; QPointer<QMenu> m_contextMenu;
QPointer<QAction> m_bakeLightsAction; QPointer<QAction> m_bakeLightsAction;
QPointer<QAction> m_editComponentAction; QPointer<QAction> m_editComponentAction;

View File

@@ -1811,12 +1811,12 @@ void NodeInstanceView::handlePuppetToCreatorCommand(const PuppetToCreatorCommand
const auto sceneState = qvariant_cast<QVariantMap>(command.data()); const auto sceneState = qvariant_cast<QVariantMap>(command.data());
if (isAttached()) if (isAttached())
model()->emitUpdateActiveScene3D(this, sceneState); model()->emitUpdateActiveScene3D(this, sceneState);
} else if (command.type() == PuppetToCreatorCommand::ActiveSplitChanged) { } else if (command.type() == PuppetToCreatorCommand::ActiveViewportChanged) {
// Active split change is a special case of active scene change // Active viewport change is a special case of active scene change
QVariantMap splitState; QVariantMap viewportState;
splitState.insert("activeSplit", command.data()); viewportState.insert("activeViewport", command.data());
if (isAttached()) if (isAttached())
model()->emitUpdateActiveScene3D(this, splitState); model()->emitUpdateActiveScene3D(this, viewportState);
} else if (command.type() == PuppetToCreatorCommand::RenderModelNodePreviewImage) { } else if (command.type() == PuppetToCreatorCommand::RenderModelNodePreviewImage) {
ImageContainer container = qvariant_cast<ImageContainer>(command.data()); ImageContainer container = qvariant_cast<ImageContainer>(command.data());
QImage image = container.image(); QImage image = container.image();

View File

@@ -44,6 +44,14 @@ inline constexpr char EDIT3D_EDIT_SHOW_CAMERA_FRUSTUM[]
= "QmlDesigner.Editor3D.ToggleCameraFrustum"; = "QmlDesigner.Editor3D.ToggleCameraFrustum";
inline constexpr char EDIT3D_EDIT_SHOW_PARTICLE_EMITTER[] inline constexpr char EDIT3D_EDIT_SHOW_PARTICLE_EMITTER[]
= "QmlDesigner.Editor3D.ToggleParticleEmitter"; = "QmlDesigner.Editor3D.ToggleParticleEmitter";
inline constexpr char EDIT3D_PRESETS[] = "QmlDesigner.Editor3D.Presets";
inline constexpr char EDIT3D_PRESET_SINGLE[] = "QmlDesigner.Editor3D.Single";
inline constexpr char EDIT3D_PRESET_QUAD[] = "QmlDesigner.Editor3D.Quad";
inline constexpr char EDIT3D_PRESET_3LEFT1RIGHT[] = "QmlDesigner.Editor3D.3Left1Right";
inline constexpr char EDIT3D_PRESET_2HORIZONTAL[] = "QmlDesigner.Editor3D.2Horizontal";
inline constexpr char EDIT3D_PRESET_2VERTICAL[] = "QmlDesigner.Editor3D.2Vertical";
inline constexpr char EDIT3D_RESET_VIEW[] = "QmlDesigner.Editor3D.ResetView"; inline constexpr char EDIT3D_RESET_VIEW[] = "QmlDesigner.Editor3D.ResetView";
inline constexpr char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeToggle"; inline constexpr char EDIT3D_PARTICLE_MODE[] = "QmlDesigner.Editor3D.ParticleViewModeToggle";
inline constexpr char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay"; inline constexpr char EDIT3D_PARTICLES_PLAY[] = "QmlDesigner.Editor3D.ParticlesPlay";

View File

@@ -8,9 +8,9 @@ Item {
id: cameraCtrl id: cameraCtrl
property var viewRoot: null property var viewRoot: null
property int splitId: -1 property int viewportId: -1
property Camera camera: view3d ? view3d.camera : null property Camera camera: view3d ? view3d.camera : null
property View3D view3d: viewRoot.editViews[splitId] property View3D view3d: viewRoot.editViews[viewportId]
property string sceneId: viewRoot.sceneId property string sceneId: viewRoot.sceneId
property vector3d _lookAtPoint property vector3d _lookAtPoint
property vector3d _pressPoint property vector3d _pressPoint
@@ -55,11 +55,11 @@ Item {
_lookAtPoint = Qt.vector3d(0, 0, 0); _lookAtPoint = Qt.vector3d(0, 0, 0);
_zoomFactor = 1; _zoomFactor = 1;
if (splitId === 1) { if (viewportId === 1) {
jumpToRotation(originGizmo.quaternionForAxis(OriginGizmo.PositiveZ)); jumpToRotation(originGizmo.quaternionForAxis(OriginGizmo.PositiveZ));
} else if (splitId === 2) { } else if (viewportId === 2) {
jumpToRotation(originGizmo.quaternionForAxis(OriginGizmo.NegativeY)); jumpToRotation(originGizmo.quaternionForAxis(OriginGizmo.NegativeY));
} else if (splitId === 3) { } else if (viewportId === 3) {
jumpToRotation(originGizmo.quaternionForAxis(OriginGizmo.NegativeX)); jumpToRotation(originGizmo.quaternionForAxis(OriginGizmo.NegativeX));
} else { } else {
camera.position = _defaultCameraPosition; camera.position = _defaultCameraPosition;
@@ -79,7 +79,7 @@ Item {
cameraState[1] = _zoomFactor; cameraState[1] = _zoomFactor;
cameraState[2] = camera.position; cameraState[2] = camera.position;
cameraState[3] = camera.rotation; cameraState[3] = camera.rotation;
_generalHelper.storeToolState(sceneId, "editCamState" + splitId, cameraState, delay); _generalHelper.storeToolState(sceneId, "editCamState" + viewportId, cameraState, delay);
} }
function focusObject(targetNodes, rotation, updateZoom, closeUp) function focusObject(targetNodes, rotation, updateZoom, closeUp)
@@ -254,12 +254,12 @@ Item {
} }
on_LookAtPointChanged: { on_LookAtPointChanged: {
viewRoot.overlayViews[splitId].lookAtGizmo.position = _lookAtPoint; viewRoot.overlayViews[viewportId].lookAtGizmo.position = _lookAtPoint;
} }
Connections { Connections {
target: _generalHelper target: _generalHelper
enabled: viewRoot.activeSplit === cameraCtrl.splitId enabled: viewRoot.activeViewport === cameraCtrl.viewportId
function onRequestCameraMove(camera, moveVec) { function onRequestCameraMove(camera, moveVec) {
if (camera === cameraCtrl.camera) { if (camera === cameraCtrl.camera) {
@@ -272,7 +272,7 @@ Item {
Image { Image {
anchors.centerIn: parent anchors.centerIn: parent
source: "qrc:///qtquickplugin/mockfiles/images/crosshair.png" source: "qrc:///qtquickplugin/mockfiles/images/crosshair.png"
visible: cameraCtrl.showCrosshairs && viewRoot.activeSplit === cameraCtrl.splitId visible: cameraCtrl.showCrosshairs && viewRoot.activeViewport === cameraCtrl.viewportId
opacity: 0.7 opacity: 0.7
} }
@@ -303,7 +303,7 @@ Item {
onPressed: (mouse) => { onPressed: (mouse) => {
if (cameraCtrl.flyMode) if (cameraCtrl.flyMode)
return; return;
viewRoot.activeSplit = cameraCtrl.splitId viewRoot.activeViewport = cameraCtrl.viewportId
if (cameraCtrl.camera && (mouse.modifiers & (Qt.AltModifier | Qt.ShiftModifier))) { if (cameraCtrl.camera && (mouse.modifiers & (Qt.AltModifier | Qt.ShiftModifier))) {
cameraCtrl._dragging = true; cameraCtrl._dragging = true;
cameraCtrl._startRotation = cameraCtrl.camera.eulerRotation; cameraCtrl._startRotation = cameraCtrl.camera.eulerRotation;
@@ -328,9 +328,9 @@ Item {
onCanceled: handleRelease() onCanceled: handleRelease()
onWheel: (wheel) => { onWheel: (wheel) => {
if (cameraCtrl.flyMode && cameraCtrl.splitId !== viewRoot.activeSplit) if (cameraCtrl.flyMode && cameraCtrl.viewportId !== viewRoot.activeViewport)
return; return;
viewRoot.activeSplit = cameraCtrl.splitId; viewRoot.activeViewport = cameraCtrl.viewportId;
if (cameraCtrl.camera) { if (cameraCtrl.camera) {
// Empirically determined divisor for nice zoom // Empirically determined divisor for nice zoom
cameraCtrl.zoomRelative(wheel.angleDelta.y / -40); cameraCtrl.zoomRelative(wheel.angleDelta.y / -40);
@@ -373,7 +373,7 @@ Item {
targetNode: cameraCtrl.camera targetNode: cameraCtrl.camera
onAxisClicked: (axis) => { onAxisClicked: (axis) => {
viewRoot.activeSplit = cameraCtrl.splitId viewRoot.activeViewport = cameraCtrl.viewportId
cameraCtrl.jumpToRotation(quaternionForAxis(axis)); cameraCtrl.jumpToRotation(quaternionForAxis(axis));
} }
} }

View File

@@ -12,7 +12,7 @@ Item {
visible: true visible: true
property Node activeScene: null property Node activeScene: null
property int activeSplit: 0 property int activeViewport: 0
property var editViews: [null, null, null, null] property var editViews: [null, null, null, null]
property var usePerspective: [true, false, false, false] property var usePerspective: [true, false, false, false]
property var overlayViews: [overlayView0, overlayView1, overlayView2, overlayView3] property var overlayViews: [overlayView0, overlayView1, overlayView2, overlayView3]
@@ -21,8 +21,8 @@ Item {
property var materialOverrides: property var materialOverrides:
[DebugSettings.None, DebugSettings.None, DebugSettings.None, DebugSettings.None] [DebugSettings.None, DebugSettings.None, DebugSettings.None, DebugSettings.None]
property var showWireframes: [false, false, false, false] property var showWireframes: [false, false, false, false]
property var activeEditView: editViews[activeSplit] property var activeEditView: editViews[activeViewport]
property var activeOverlayView: overlayViews[activeSplit] property var activeOverlayView: overlayViews[activeViewport]
property string sceneId property string sceneId
property bool showEditLight: false property bool showEditLight: false
@@ -37,12 +37,56 @@ Item {
property color backgroundGradientColorStart: "#222222" property color backgroundGradientColorStart: "#222222"
property color backgroundGradientColorEnd: "#999999" property color backgroundGradientColorEnd: "#999999"
property color gridColor: "#cccccc" property color gridColor: "#cccccc"
property color viewportBorderColor: "#aaaaaaaa"
property bool syncEnvBackground: true property bool syncEnvBackground: true
property bool splitView: false property string activePreset: "Single"
property bool flyMode: false property bool flyMode: false
property bool showCameraSpeed: false property bool showCameraSpeed: false
property string cameraViewMode property string cameraViewMode
// The presets used to customize the display of the viewports
property var viewportPresets: {
"Single": {
numViewports: 1,
viewRects: [
{ x: 0.0, y: 0.0, width: 1.0, height: 1.0 }
]
},
"Quad": {
numViewports: 4,
viewRects: [
{ x: 0.0, y: 0.0, width: 0.5, height: 0.5 },
{ x: 0.5, y: 0.0, width: 0.5, height: 0.5 },
{ x: 0.0, y: 0.5, width: 0.5, height: 0.5 },
{ x: 0.5, y: 0.5, width: 0.5, height: 0.5 }
]
},
"3Left1Right": {
numViewports: 4,
viewRects: [
{ x: 0.0, y: 0.0, width: 0.25, height: 0.33 },
{ x: 0.0, y: 0.33, width: 0.25, height: 0.34 },
{ x: 0.0, y: 0.67, width: 0.25, height: 0.33 },
{ x: 0.25, y: 0.0, width: 0.75, height: 1.0 }
]
},
"2Horizontal": {
numViewports: 2,
viewRects: [
{ x: 0.0, y: 0.0, width: 1.0, height: 0.5 },
{ x: 0.0, y: 0.5, width: 1.0, height: 0.5 }
]
},
"2Vertical": {
numViewports: 2,
viewRects: [
{ x: 0.0, y: 0.0, width: 0.5, height: 1.0 },
{ x: 0.5, y: 0.0, width: 0.5, height: 1.0 }
]
}
//TODO: reset of presets
};
enum SelectionMode { Item, Group } enum SelectionMode { Item, Group }
enum TransformMode { Move, Rotate, Scale } enum TransformMode { Move, Rotate, Scale }
@@ -64,7 +108,7 @@ Item {
signal commitObjectProperty(var objects, var propNames) signal commitObjectProperty(var objects, var propNames)
signal changeObjectProperty(var objects, var propNames) signal changeObjectProperty(var objects, var propNames)
signal notifyActiveSceneChange() signal notifyActiveSceneChange()
signal notifyActiveSplitChange(int index) signal notifyActiveViewportChange(int index)
onUsePerspectiveChanged: _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective) onUsePerspectiveChanged: _generalHelper.storeToolState(sceneId, "usePerspective", usePerspective)
onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight) onShowEditLightChanged: _generalHelper.storeToolState(sceneId, "showEditLight", showEditLight)
@@ -81,14 +125,14 @@ Item {
onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode); onTransformModeChanged: _generalHelper.storeToolState(sceneId, "transformMode", transformMode);
onMaterialOverridesChanged: _generalHelper.storeToolState(sceneId, "matOverride", materialOverrides); onMaterialOverridesChanged: _generalHelper.storeToolState(sceneId, "matOverride", materialOverrides);
onShowWireframesChanged: _generalHelper.storeToolState(sceneId, "showWireframe", showWireframes); onShowWireframesChanged: _generalHelper.storeToolState(sceneId, "showWireframe", showWireframes);
onSplitViewChanged: { onActivePresetChanged: {
_generalHelper.storeToolState(sceneId, "splitView", splitView); _generalHelper.storeToolState(sceneId, "activePreset", activePreset);
_generalHelper.requestOverlayUpdate(); _generalHelper.requestOverlayUpdate();
} }
onActiveSplitChanged: { onActiveViewportChanged: {
_generalHelper.storeToolState(sceneId, "activeSplit", activeSplit); _generalHelper.storeToolState(sceneId, "activeViewport", activeViewport);
cameraControls[activeSplit].forceActiveFocus(); cameraControls[activeViewport].forceActiveFocus();
notifyActiveSplitChange(activeSplit); notifyActiveViewportChange(activeViewport);
} }
onActiveSceneChanged: updateActiveScene() onActiveSceneChanged: updateActiveScene()
@@ -141,7 +185,7 @@ Item {
selectionBoxCount = 0; selectionBoxCount = 0;
editViewsChanged(); editViewsChanged();
cameraControls[activeSplit].forceActiveFocus(); cameraControls[activeViewport].forceActiveFocus();
return true; return true;
} }
return false; return false;
@@ -234,7 +278,7 @@ Item {
} else if (selectedNodes.length > 0 && selectionBoxCount > 0) { } else if (selectedNodes.length > 0 && selectionBoxCount > 0) {
boxModels.push(activeEditView.selectionBoxes[0].model); boxModels.push(activeEditView.selectionBoxes[0].model);
} }
cameraControls[activeSplit].focusObject( cameraControls[activeViewport].focusObject(
boxModels, activeEditView.camera.eulerRotation, true, false); boxModels, activeEditView.camera.eulerRotation, true, false);
} }
} }
@@ -242,7 +286,7 @@ Item {
function alignCamerasToView(cameraNodes) function alignCamerasToView(cameraNodes)
{ {
if (activeEditView) { if (activeEditView) {
cameraControls[activeSplit].alignCameras(cameraNodes); cameraControls[activeViewport].alignCameras(cameraNodes);
var propertyNames = ["position", "eulerRotation"]; var propertyNames = ["position", "eulerRotation"];
viewRoot.changeObjectProperty(cameraNodes, propertyNames); viewRoot.changeObjectProperty(cameraNodes, propertyNames);
viewRoot.commitObjectProperty(cameraNodes, propertyNames); viewRoot.commitObjectProperty(cameraNodes, propertyNames);
@@ -252,7 +296,7 @@ Item {
function alignViewToCamera(cameraNodes) function alignViewToCamera(cameraNodes)
{ {
if (activeEditView) if (activeEditView)
cameraControls[activeSplit].alignView(cameraNodes); cameraControls[activeViewport].alignView(cameraNodes);
} }
function updateBackgroundColors(colors) function updateBackgroundColors(colors)
@@ -388,15 +432,16 @@ Item {
viewRoot.showCameraSpeed = false; viewRoot.showCameraSpeed = false;
} }
if ("splitView" in toolStates) if ("activePreset" in toolStates)
splitView = toolStates.splitView; activePreset = toolStates.activePreset;
else if (resetToDefault) else if (resetToDefault)
splitView = false; activePreset = "Quad";
applyViewportPreset(activePreset)
if ("activeSplit" in toolStates) if ("activeViewport" in toolStates)
activeSplit = toolStates.activeSplit; activeViewport = toolStates.activeViewport;
else if (resetToDefault) else if (resetToDefault)
activeSplit = 0; activeViewport = 0;
if ("showWireframe" in toolStates) if ("showWireframe" in toolStates)
showWireframes = toolStates.showWireframe; showWireframes = toolStates.showWireframe;
@@ -424,8 +469,8 @@ Item {
_generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation) _generalHelper.storeToolState(sceneId, "globalOrientation", globalOrientation)
_generalHelper.storeToolState(sceneId, "selectionMode", selectionMode); _generalHelper.storeToolState(sceneId, "selectionMode", selectionMode);
_generalHelper.storeToolState(sceneId, "transformMode", transformMode); _generalHelper.storeToolState(sceneId, "transformMode", transformMode);
_generalHelper.storeToolState(sceneId, "splitView", splitView) _generalHelper.storeToolState(sceneId, "activePreset", activePreset)
_generalHelper.storeToolState(sceneId, "activeSplit", activeSplit) _generalHelper.storeToolState(sceneId, "activeViewport", activeViewport)
_generalHelper.storeToolState(sceneId, "showWireframe", showWireframes) _generalHelper.storeToolState(sceneId, "showWireframe", showWireframes)
_generalHelper.storeToolState(sceneId, "matOverride", materialOverrides) _generalHelper.storeToolState(sceneId, "matOverride", materialOverrides)
@@ -527,6 +572,7 @@ Item {
selectionChanged(newSelection); selectionChanged(newSelection);
} }
//TODO: only update the active viewport views
function addLightGizmo(scene, obj) function addLightGizmo(scene, obj)
{ {
for (var i = 0; i < 4; ++i) for (var i = 0; i < 4; ++i)
@@ -617,64 +663,95 @@ Item {
overlayViews[i].updateReflectionProbeGizmoScene(scene, obj); overlayViews[i].updateReflectionProbeGizmoScene(scene, obj);
} }
function resolveSplitPoint(x, y) function resolveViewportPoint(x, y)
{ {
if (!splitView || activeSplit === 0) let rect = viewRects[activeViewport];
// Check invisible or out or range, then fallback to original origin
if (!rect || !rect.visible)
return Qt.point(x, y); return Qt.point(x, y);
if (activeSplit === 1) // Transform topleft of the active viewport to be the origin
return Qt.point(x - viewContainer.width / 2, y); return Qt.point(x - rect.x, y - rect.y);
else if (activeSplit === 2)
return Qt.point(x, y - viewContainer.height / 2);
else
return Qt.point(x - viewContainer.width / 2, y - viewContainer.height / 2);
} }
function updateActiveSplit(x, y) function updateActiveViewport(x, y)
{ {
if (splitView) { for (let i = 0; i < 4; ++i) {
if (x <= viewContainer.width / 2) { let rect = viewRects[i];
if (y <= viewContainer.height / 2) if (!rect.visible)
activeSplit = 0; continue;
else
activeSplit = 2; if (x >= rect.x && x <= rect.x + rect.width
} else { && y >= rect.y && y <= rect.y + rect.height) {
if (y <= viewContainer.height / 2) activeViewport = i;
activeSplit = 1; return;
else
activeSplit = 3;
} }
} }
// TODO: if click outside all visible viewRects, do nothing
// or reset to e.g. activeVireport = -1 or 0
} }
function gizmoAt(x, y) function gizmoAt(x, y)
{ {
updateActiveSplit(x, y); updateActiveViewport(x, y);
let splitPoint = resolveSplitPoint(x, y); let viewportPoint = resolveViewportPoint(x, y);
return activeOverlayView.gizmoAt(splitPoint.x, splitPoint.y); return activeOverlayView.gizmoAt(viewportPoint.x, viewportPoint.y);
} }
function rotateEditCamera(angles) function rotateEditCamera(angles)
{ {
cameraControls[activeSplit].rotateCamera(angles); cameraControls[activeViewport].rotateCamera(angles);
} }
function moveEditCamera(amounts) function moveEditCamera(amounts)
{ {
cameraControls[activeSplit].moveCamera(amounts); cameraControls[activeViewport].moveCamera(amounts);
}
// Update viewports based on selected preset
function applyViewportPreset(presetName)
{
let preset = viewportPresets[presetName];
if (!preset)
return;
let count = preset.numViewports;
for (let i = 0; i < 4; ++i) {
if (i < count) {
viewRects[i].visible = true;
viewRects[i].x = preset.viewRects[i].x * viewContainer.width;
viewRects[i].y = preset.viewRects[i].y * viewContainer.height;
viewRects[i].width = preset.viewRects[i].width * viewContainer.width;
viewRects[i].height = preset.viewRects[i].height * viewContainer.height;
} else {
viewRects[i].visible = false;
}
}
//TODO: Do we need this here?
cameraView.updateSnapping();
} }
Component.onCompleted: { Component.onCompleted: {
createEditViews(); createEditViews();
selectObjects([]); selectObjects([]);
applyViewportPreset(activePreset)
// Work-around the fact that the projection matrix for the camera is not calculated until // Work-around the fact that the projection matrix for the camera is not calculated until
// the first frame is rendered, so any initial calls to mapFrom3DScene() will fail. // the first frame is rendered, so any initial calls to mapFrom3DScene() will fail.
_generalHelper.requestOverlayUpdate(); _generalHelper.requestOverlayUpdate();
} }
onWidthChanged: _generalHelper.requestOverlayUpdate() onWidthChanged: {
onHeightChanged: _generalHelper.requestOverlayUpdate() applyViewportPreset(activePreset)
_generalHelper.requestOverlayUpdate()
}
onHeightChanged: {
applyViewportPreset(activePreset)
_generalHelper.requestOverlayUpdate()
}
Connections { Connections {
target: _generalHelper target: _generalHelper
@@ -715,9 +792,9 @@ Item {
} }
// Shared nodes of the overlay, set as importScene on all overlay views. // Shared nodes of the overlay, set as importScene on all overlay views.
// Content here can be used as is on all splits. // Content here can be used as is on all viewports.
// Nodes that utilize autoscaling or otherwise need to have different appearance on each split // Nodes that utilize autoscaling or otherwise need to have different appearance on each viewport
// need to have separate copy on each split. // need to have separate copy on each viewport.
Node { Node {
id: overlayScene id: overlayScene
@@ -743,12 +820,9 @@ Item {
Rectangle { Rectangle {
id: viewRect0 id: viewRect0
width: viewRoot.splitView ? parent.width / 2 : parent.width
height: viewRoot.splitView ? parent.height / 2 : parent.height
x: 0
y: 0
visible: viewRoot.splitView || viewRoot.activeSplit == 0
gradient: bgGradient gradient: bgGradient
border.width: 1
border.color: viewportBorderColor
OverlayView3D { OverlayView3D {
id: overlayView0 id: overlayView0
editView: viewRoot.editViews[0] editView: viewRoot.editViews[0]
@@ -765,18 +839,15 @@ Item {
EditCameraController { EditCameraController {
id: cameraControl0 id: cameraControl0
viewRoot: viewRoot viewRoot: viewRoot
splitId: 0 viewportId: 0
} }
} }
Rectangle { Rectangle {
id: viewRect1 id: viewRect1
width: viewRoot.splitView ? parent.width / 2 : parent.width
height: viewRoot.splitView ? parent.height / 2 : parent.height
x: viewRoot.splitView ? parent.width / 2 : 0
y: 0
visible: viewRoot.splitView || viewRoot.activeSplit == 1
gradient: bgGradient gradient: bgGradient
border.width: 1
border.color: viewportBorderColor
OverlayView3D { OverlayView3D {
id: overlayView1 id: overlayView1
editView: viewRoot.editViews[1] editView: viewRoot.editViews[1]
@@ -793,18 +864,15 @@ Item {
EditCameraController { EditCameraController {
id: cameraControl1 id: cameraControl1
viewRoot: viewRoot viewRoot: viewRoot
splitId: 1 viewportId: 1
} }
} }
Rectangle { Rectangle {
id: viewRect2 id: viewRect2
width: viewRoot.splitView ? parent.width / 2 : parent.width
height: viewRoot.splitView ? parent.height / 2 : parent.height
x: 0
y: viewRoot.splitView ? parent.height / 2 : 0
visible: viewRoot.splitView || viewRoot.activeSplit == 2
gradient: bgGradient gradient: bgGradient
border.width: 1
border.color: viewportBorderColor
OverlayView3D { OverlayView3D {
id: overlayView2 id: overlayView2
editView: viewRoot.editViews[2] editView: viewRoot.editViews[2]
@@ -821,18 +889,15 @@ Item {
EditCameraController { EditCameraController {
id: cameraControl2 id: cameraControl2
viewRoot: viewRoot viewRoot: viewRoot
splitId: 2 viewportId: 2
} }
} }
Rectangle { Rectangle {
id: viewRect3 id: viewRect3
width: viewRoot.splitView ? parent.width / 2 : parent.width
height: viewRoot.splitView ? parent.height / 2 : parent.height
x: viewRoot.splitView ? parent.width / 2 : 0
y: viewRoot.splitView ? parent.height / 2 : 0
visible: viewRoot.splitView || viewRoot.activeSplit == 3
gradient: bgGradient gradient: bgGradient
border.width: 1
border.color: viewportBorderColor
OverlayView3D { OverlayView3D {
id: overlayView3 id: overlayView3
editView: viewRoot.editViews[3] editView: viewRoot.editViews[3]
@@ -849,44 +914,21 @@ Item {
EditCameraController { EditCameraController {
id: cameraControl3 id: cameraControl3
viewRoot: viewRoot viewRoot: viewRoot
splitId: 3 viewportId: 3
} }
} }
// Active viewport highlight
Rectangle { Rectangle {
// Vertical border between splits visible: activePreset !== "Single" && viewRects[viewRoot.activeViewport].visible
visible: viewRoot.splitView x: viewRects[viewRoot.activeViewport].x
x: parent.width / 2 y: viewRects[viewRoot.activeViewport].y
y: 0 width: viewRects[viewRoot.activeViewport].width
width: 1 height: viewRects[viewRoot.activeViewport].height
height: parent.height
border.width: 1
border.color: "#aaaaaa"
}
Rectangle {
// Horizontal border between splits
visible: viewRoot.splitView
x: 0
y: parent.height / 2
height: 1
width: parent.width
border.width: 1
border.color: "#aaaaaa"
}
Rectangle {
// Active split highlight
visible: viewRoot.splitView
x: viewRects[viewRoot.activeSplit].x
y: viewRects[viewRoot.activeSplit].y
height: viewRects[viewRoot.activeSplit].height
+ (viewRoot.activeSplit === 0 || viewRoot.activeSplit === 1 ? 1 : 0)
width: viewRects[viewRoot.activeSplit].width
+ (viewRoot.activeSplit === 0 || viewRoot.activeSplit === 2 ? 1 : 0)
border.width: 2 border.width: 2
border.color: "#57B9FC" border.color: "#57B9FC"
color: "transparent" color: "transparent"
z: 1000 // Edge case to make sure selection rect drawn over everything
} }
MouseArea { MouseArea {
@@ -903,20 +945,20 @@ Item {
if (viewRoot.flyMode) if (viewRoot.flyMode)
return; return;
viewRoot.updateActiveSplit(mouse.x, mouse.y); viewRoot.updateActiveViewport(mouse.x, mouse.y);
let splitPoint = viewRoot.resolveSplitPoint(mouse.x, mouse.y); let viewportPoint = viewRoot.resolveViewportPoint(mouse.x, mouse.y);
if (viewRoot.activeEditView) { if (viewRoot.activeEditView) {
// First pick overlay to check for hits there // First pick overlay to check for hits there
var pickResult = _generalHelper.pickViewAt(activeOverlayView, var pickResult = _generalHelper.pickViewAt(activeOverlayView,
splitPoint.x, splitPoint.y); viewportPoint.x, viewportPoint.y);
var resolvedResult = _generalHelper.resolvePick(pickResult.objectHit); var resolvedResult = _generalHelper.resolvePick(pickResult.objectHit);
if (!resolvedResult) { if (!resolvedResult) {
// No hits from overlay view, pick the main scene // No hits from overlay view, pick the main scene
pickResult = _generalHelper.pickViewAt(viewRoot.activeEditView, pickResult = _generalHelper.pickViewAt(viewRoot.activeEditView,
splitPoint.x, splitPoint.y); viewportPoint.x, viewportPoint.y);
resolvedResult = _generalHelper.resolvePick(pickResult.objectHit); resolvedResult = _generalHelper.resolvePick(pickResult.objectHit);
} }
@@ -940,17 +982,17 @@ Item {
} }
onPositionChanged: (mouse) => { onPositionChanged: (mouse) => {
if (freeDraggerArea) { if (freeDraggerArea) {
let splitPoint = viewRoot.resolveSplitPoint(mouse.x, mouse.y); let viewportPoint = viewRoot.resolveViewportPoint(mouse.x, mouse.y);
let splitPress = viewRoot.resolveSplitPoint(pressPoint.x, pressPoint.y); let viewportPress = viewRoot.resolveViewportPoint(pressPoint.x, pressPoint.y);
if (initialMoveBlock && Math.abs(splitPress.x - splitPoint.x) if (initialMoveBlock && Math.abs(viewportPress.x - viewportPoint.x)
+ Math.abs(splitPress.y - splitPoint.y) > 10) { + Math.abs(viewportPress.y - viewportPoint.y) > 10) {
// Don't force press event at actual press, as that puts the gizmo // Don't force press event at actual press, as that puts the gizmo
// in free-dragging state, which is bad UX if drag is not actually done // in free-dragging state, which is bad UX if drag is not actually done
freeDraggerArea.forcePressEvent(splitPress.x, splitPress.y); freeDraggerArea.forcePressEvent(viewportPress.x, viewportPress.y);
freeDraggerArea.forceMoveEvent(splitPoint.x, splitPoint.y); freeDraggerArea.forceMoveEvent(viewportPoint.x, viewportPoint.y);
initialMoveBlock = false; initialMoveBlock = false;
} else { } else {
freeDraggerArea.forceMoveEvent(splitPoint.x, splitPoint.y); freeDraggerArea.forceMoveEvent(viewportPoint.x, viewportPoint.y);
} }
} }
} }
@@ -959,11 +1001,11 @@ Item {
{ {
if (freeDraggerArea) { if (freeDraggerArea) {
if (initialMoveBlock) { if (initialMoveBlock) {
let splitPress = viewRoot.resolveSplitPoint(pressPoint.x, pressPoint.y); let viewportPress = viewRoot.resolveViewportPoint(pressPoint.x, pressPoint.y);
freeDraggerArea.forceReleaseEvent(splitPress.x, splitPress.y); freeDraggerArea.forceReleaseEvent(viewportPress.x, viewportPress.y);
} else { } else {
let splitPoint = viewRoot.resolveSplitPoint(mouse.x, mouse.y); let viewportPoint = viewRoot.resolveViewportPoint(mouse.x, mouse.y);
freeDraggerArea.forceReleaseEvent(splitPoint.x, splitPoint.y); freeDraggerArea.forceReleaseEvent(viewportPoint.x, viewportPoint.y);
} }
freeDraggerArea = null; freeDraggerArea = null;
} }
@@ -1107,19 +1149,17 @@ Item {
viewPortSize: Qt.size(viewRoot.viewPortRect.width, viewRoot.viewPortRect.height) viewPortSize: Qt.size(viewRoot.viewPortRect.width, viewRoot.viewPortRect.height)
function updateSnapping() { function updateSnapping() {
if (!viewRoot.splitView) const rect = viewRoot.viewRects[viewRoot.activeViewport];
cameraView.snapLeft = true if (!rect || !rect.visible)
else if (viewRoot.activeSplit === 2) return;
cameraView.snapLeft = false
else if (viewRoot.activeSplit === 3) const centerX = rect.x + rect.width / 2;
cameraView.snapLeft = true cameraView.snapLeft = centerX < viewContainer.width / 2;
} }
Connections { Connections {
target: viewRoot target: viewRoot
onActiveViewportChanged: cameraView.updateSnapping()
onSplitViewChanged: cameraView.updateSnapping()
onActiveSplitChanged: cameraView.updateSnapping()
} }
} }
} }

View File

@@ -16,7 +16,7 @@ View3D {
property var viewRoot: null property var viewRoot: null
property View3D editView: null property View3D editView: null
property bool isActive: viewRoot.overlayViews[viewRoot.activeSplit] === overlayView property bool isActive: viewRoot.overlayViews[viewRoot.activeViewport] === overlayView
property var lightIconGizmos: [] property var lightIconGizmos: []
property var cameraGizmos: [] property var cameraGizmos: []
@@ -629,7 +629,7 @@ View3D {
scale: lookAtAutoScale.getScale(Qt.vector3d(10, 10, 10)) scale: lookAtAutoScale.getScale(Qt.vector3d(10, 10, 10))
visible: overlayView.viewRoot.showLookAt visible: overlayView.viewRoot.showLookAt
&& overlayView.isActive && overlayView.isActive
&& !overlayView.viewRoot.cameraControls[viewRoot.activeSplit].showCrosshairs && !overlayView.viewRoot.cameraControls[viewRoot.activeViewport].showCrosshairs
} }
} }
} }

View File

@@ -956,9 +956,9 @@ void Qt5InformationNodeInstanceServer::handleActiveSceneChange()
#endif #endif
} }
void Qt5InformationNodeInstanceServer::handleActiveSplitChange(int index) void Qt5InformationNodeInstanceServer::handleActiveViewportChange(int index)
{ {
nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::ActiveSplitChanged, nodeInstanceClient()->handlePuppetToCreatorCommand({PuppetToCreatorCommand::ActiveViewportChanged,
index}); index});
} }
@@ -1966,8 +1966,8 @@ void Qt5InformationNodeInstanceServer::setup3DEditView(
this, SLOT(handleObjectPropertyChange(QVariant, QVariant))); this, SLOT(handleObjectPropertyChange(QVariant, QVariant)));
QObject::connect(m_editView3DData.rootItem, SIGNAL(notifyActiveSceneChange()), QObject::connect(m_editView3DData.rootItem, SIGNAL(notifyActiveSceneChange()),
this, SLOT(handleActiveSceneChange())); this, SLOT(handleActiveSceneChange()));
QObject::connect(m_editView3DData.rootItem, SIGNAL(notifyActiveSplitChange(int)), QObject::connect(m_editView3DData.rootItem, SIGNAL(notifyActiveViewportChange(int)),
this, SLOT(handleActiveSplitChange(int))); this, SLOT(handleActiveViewportChange(int)));
QObject::connect(&m_propertyChangeTimer, &QTimer::timeout, QObject::connect(&m_propertyChangeTimer, &QTimer::timeout,
this, &Qt5InformationNodeInstanceServer::handleObjectPropertyChangeTimeout); this, &Qt5InformationNodeInstanceServer::handleObjectPropertyChangeTimeout);
QObject::connect(&m_selectionChangeTimer, &QTimer::timeout, QObject::connect(&m_selectionChangeTimer, &QTimer::timeout,
@@ -2599,6 +2599,9 @@ void Qt5InformationNodeInstanceServer::view3DAction([[maybe_unused]] const View3
case View3DActionType::SyncEnvBackground: case View3DActionType::SyncEnvBackground:
updatedToolState.insert("syncEnvBackground", command.isEnabled()); updatedToolState.insert("syncEnvBackground", command.isEnabled());
break; break;
case View3DActionType::ViewportPreset:
updatedToolState.insert("activePreset", command.value());
break;
#ifdef QUICK3D_PARTICLES_MODULE #ifdef QUICK3D_PARTICLES_MODULE
case View3DActionType::ShowParticleEmitter: case View3DActionType::ShowParticleEmitter:
updatedToolState.insert("showParticleEmitter", command.isEnabled()); updatedToolState.insert("showParticleEmitter", command.isEnabled());
@@ -2637,8 +2640,8 @@ void Qt5InformationNodeInstanceServer::view3DAction([[maybe_unused]] const View3
getNodeAtMainScenePos(data[0].toPointF(), qint32(data[1].toInt())); getNodeAtMainScenePos(data[0].toPointF(), qint32(data[1].toInt()));
return; return;
} }
case View3DActionType::SplitViewToggle: case View3DActionType::ViewportViewToggle:
updatedToolState.insert("splitView", command.isEnabled()); updatedToolState.insert("viewportView", command.isEnabled());
break; break;
case View3DActionType::FlyModeToggle: case View3DActionType::FlyModeToggle:
updatedToolState.insert("flyMode", command.isEnabled()); updatedToolState.insert("flyMode", command.isEnabled());

View File

@@ -66,7 +66,7 @@ private slots:
void handleObjectPropertyCommit(const QVariant &objects, const QVariant &propNames); void handleObjectPropertyCommit(const QVariant &objects, const QVariant &propNames);
void handleObjectPropertyChange(const QVariant &objects, const QVariant &propNames); void handleObjectPropertyChange(const QVariant &objects, const QVariant &propNames);
void handleActiveSceneChange(); void handleActiveSceneChange();
void handleActiveSplitChange(int index); void handleActiveViewportChange(int index);
void handleToolStateChanged(const QString &sceneId, const QString &tool, void handleToolStateChanged(const QString &sceneId, const QString &tool,
const QVariant &toolState); const QVariant &toolState);
void handleView3DSizeChange(); void handleView3DSizeChange();