QmlDesigner: Remember 3D view splitter positions

Splitter positions for each split preset in 3D view are stored in tool
state of 3D scene, so they are remembered like all other tool states in
3D view.

Refactored split presets to use enums instead of strings to facilitate
tool state handling. It should also be slightly more efficient.

Fixes: QDS-15343
Change-Id: I25619f2df2530875f73762bb6f65a133e326bff3
Reviewed-by: Mahmoud Badri <mahmoud.badri@qt.io>
This commit is contained in:
Miikka Heikkinen
2025-05-15 15:18:02 +03:00
parent e0bd1e0ca9
commit 17c938a385
4 changed files with 173 additions and 139 deletions

View File

@@ -274,9 +274,9 @@ void Edit3DView::updateActiveScene3D(const QVariantMap &sceneState)
m_syncEnvBackgroundAction->action()->setChecked(false);
if (sceneState.contains(activePresetKey))
syncActivePresetCheckedState(sceneState[activePresetKey].toString());
syncActivePresetCheckedState(static_cast<ViewPreset>(sceneState[activePresetKey].toInt()));
else
syncActivePresetCheckedState("Single");
syncActivePresetCheckedState(ViewPreset::Single);
// Selection context change updates visible and enabled states
SelectionContext selectionContext(this);
@@ -729,14 +729,14 @@ void Edit3DView::createViewportPresetActions()
auto createViewportPresetAction = [this](std::unique_ptr<Edit3DAction> &targetAction,
const QByteArray &id,
const QString &label,
const QString &opCode,
const ViewPreset &opCode,
const QIcon &icon,
bool isChecked) {
auto operation = [this, &targetAction, opCode](const SelectionContext &) {
for (Edit3DAction *action : std::as_const(m_viewportPresetActions))
action->action()->setChecked(action->menuId() == targetAction->menuId());
emitView3DAction(View3DActionType::ViewportPreset, opCode);
emitView3DAction(View3DActionType::ViewportPreset, int(opCode));
};
targetAction = std::make_unique<Edit3DAction>(
@@ -752,15 +752,15 @@ void Edit3DView::createViewportPresetActions()
};
createViewportPresetAction(m_viewportPresetSingleAction, Constants::EDIT3D_PRESET_SINGLE,
Tr::tr("Single"), "Single", contextIcon(DesignerIcons::MultiViewPort1Icon), true);
Tr::tr("Single"), ViewPreset::Single, contextIcon(DesignerIcons::MultiViewPort1Icon), true);
createViewportPresetAction(m_viewportPresetQuadAction, Constants::EDIT3D_PRESET_QUAD,
Tr::tr("Quad"), "Quad", contextIcon(DesignerIcons::MultiViewPort2x2Icon), false);
Tr::tr("Quad"), ViewPreset::Quad, contextIcon(DesignerIcons::MultiViewPort2x2Icon), false);
createViewportPresetAction(m_viewportPreset3Left1RightAction, Constants::EDIT3D_PRESET_3LEFT1RIGHT,
Tr::tr("3 Left 1 Right"), "3Left1Right", contextIcon(DesignerIcons::MultiViewPort3plus1Icon), false);
Tr::tr("3 Left 1 Right"), ViewPreset::ThreeLeftOneRight, contextIcon(DesignerIcons::MultiViewPort3plus1Icon), false);
createViewportPresetAction(m_viewportPreset2HorizontalAction, Constants::EDIT3D_PRESET_2HORIZONTAL,
Tr::tr("2 Horizontal"), "2Horizontal", contextIcon(DesignerIcons::MultiViewPort2hlIcon), false);
Tr::tr("2 Horizontal"), ViewPreset::TwoHorizontal, contextIcon(DesignerIcons::MultiViewPort2hlIcon), false);
createViewportPresetAction(m_viewportPreset2VerticalAction, Constants::EDIT3D_PRESET_2VERTICAL,
Tr::tr("2 Vertical"), "2Vertical", contextIcon(DesignerIcons::MultiViewPort2vlIcon), false);
Tr::tr("2 Vertical"), ViewPreset::TwoVertical, contextIcon(DesignerIcons::MultiViewPort2vlIcon), false);
m_viewportPresetActions << m_viewportPresetSingleAction.get();
m_viewportPresetActions << m_viewportPresetQuadAction.get();
@@ -921,13 +921,13 @@ void Edit3DView::syncCameraSpeedToNewView()
setCameraSpeedAuxData(speed, multiplier);
}
void Edit3DView::syncActivePresetCheckedState(const QString &preset)
void Edit3DView::syncActivePresetCheckedState(ViewPreset preset)
{
m_viewportPresetSingleAction->action()->setChecked(preset == "Single");
m_viewportPresetQuadAction->action()->setChecked(preset == "Quad");
m_viewportPreset3Left1RightAction->action()->setChecked(preset == "3Left1Right");
m_viewportPreset2HorizontalAction->action()->setChecked(preset == "2Horizontal");
m_viewportPreset2VerticalAction->action()->setChecked(preset == "2Vertical");
m_viewportPresetSingleAction->action()->setChecked(preset == ViewPreset::Single);
m_viewportPresetQuadAction->action()->setChecked(preset == ViewPreset::Quad);
m_viewportPreset3Left1RightAction->action()->setChecked(preset == ViewPreset::ThreeLeftOneRight);
m_viewportPreset2HorizontalAction->action()->setChecked(preset == ViewPreset::TwoHorizontal);
m_viewportPreset2VerticalAction->action()->setChecked(preset == ViewPreset::TwoVertical);
}
QmlObjectNode Edit3DView::currentSceneEnv()

View File

@@ -135,6 +135,14 @@ private:
None
};
enum class ViewPreset {
Single,
Quad,
ThreeLeftOneRight,
TwoHorizontal,
TwoVertical
};
void registerEdit3DAction(Edit3DAction *action);
void createEdit3DWidget();
@@ -151,7 +159,7 @@ private:
void createViewportPresetActions();
void createSeekerSliderAction();
void syncCameraSpeedToNewView();
void syncActivePresetCheckedState(const QString &preset);
void syncActivePresetCheckedState(ViewPreset preset);
QmlObjectNode currentSceneEnv();
void storeCurrentSceneEnvironment();

View File

@@ -11,6 +11,14 @@ Item {
height: 768
visible: true
enum ViewPreset {
Single,
Quad,
ThreeLeftOneRight,
TwoHorizontal,
TwoVertical
}
property Node activeScene: null
property int activeViewport: 0
property var editViews: [null, null, null, null]
@@ -39,53 +47,40 @@ Item {
property color gridColor: "#cccccc"
property color viewportBorderColor: "#aaaaaaaa"
property bool syncEnvBackground: true
property string activePreset: "Single"
property int activePreset: EditView3D.ViewPreset.Single
property bool flyMode: false
property bool showCameraSpeed: false
property string cameraViewMode
property int mouseCursor: -1
// 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.25, y: 0.0, width: 0.75, height: 1.0 },
{ x: 0.0, y: 0.0, width: 0.25, height: 0.33 },
{ x: 0.0, y: 0.3333, width: 0.25, height: 0.34 },
{ x: 0.0, y: 0.6667, width: 0.25, height: 0.33 }
]
},
"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 }
]
}
};
readonly property var viewportPresets: [
[ // Single
{ x: 0.0, y: 0.0, width: 1.0, height: 1.0 }
],
[ // Quad
{ 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 }
],
[ // ThreeLeftOneRight
{ x: 0.25, y: 0.0, width: 0.75, height: 1.0 },
{ x: 0.0, y: 0.0, width: 0.25, height: 0.33 },
{ x: 0.0, y: 0.3333, width: 0.25, height: 0.34 },
{ x: 0.0, y: 0.6667, width: 0.25, height: 0.33 }
],
[ // TwoHorizontal
{ x: 0.0, y: 0.0, width: 1.0, height: 0.5 },
{ x: 0.0, y: 0.5, width: 1.0, height: 0.5 }
],
[ // TwoVertical
{ x: 0.0, y: 0.0, width: 0.5, height: 1.0 },
{ x: 0.5, y: 0.0, width: 0.5, height: 1.0 }
]
]
property var activeDividers: defaultDividers()
enum SelectionMode { Item, Group }
enum TransformMode { Move, Rotate, Scale }
@@ -102,13 +97,9 @@ Item {
property Node activeParticleSystem: null
property bool shuttingDown: false
// Always normalized [0.0…1.0]
property real dividerX: 0.5 // Vertical split (left & right)
property real dividerY: 0.5 // Horizontal split (top & bottom)
// Only used in “3Left1Right”
property real dividerY1: 0.3333 // first horizontal in left column
property real dividerY2: 0.6667 // secnd horizontal in left column
// Only used with ThreeLeftOneRight, never change
readonly property real dividerY1: 0.3333 // first horizontal in left column
readonly property real dividerY2: 0.6667 // secnd horizontal in left column
property real fps: 0
@@ -451,10 +442,24 @@ Item {
else if (resetToDefault)
activeViewport = 0;
if ("activePreset" in toolStates)
activePreset = toolStates.activePreset;
else if (resetToDefault)
activePreset = "Single";
if ("activePreset" in toolStates) {
if (toolStates.activePreset < 0 || toolStates.activePreset > 4)
activePreset = EditView3D.ViewPreset.Single;
else
activePreset = toolStates.activePreset;
} else if (resetToDefault) {
activePreset = EditView3D.ViewPreset.Single;
}
if ("activeDividers" in toolStates) {
activeDividers = toolStates.activeDividers;
updateViewRects()
updateSplitResizers()
} else if (resetToDefault) {
activeDividers = defaultDividers()
updateViewRects()
updateSplitResizers()
}
if ("showWireframe" in toolStates)
showWireframes = toolStates.showWireframe;
@@ -483,6 +488,7 @@ Item {
_generalHelper.storeToolState(sceneId, "selectionMode", selectionMode);
_generalHelper.storeToolState(sceneId, "transformMode", transformMode);
_generalHelper.storeToolState(sceneId, "activePreset", activePreset)
_generalHelper.storeToolState(sceneId, "activeDividers", activeDividers)
_generalHelper.storeToolState(sceneId, "activeViewport", activeViewport)
_generalHelper.storeToolState(sceneId, "showWireframe", showWireframes)
_generalHelper.storeToolState(sceneId, "matOverride", materialOverrides)
@@ -725,14 +731,8 @@ Item {
function updateSplitResizers()
{
verticalResizer.x = dividerX * viewContainer.width - verticalResizer.grabSize
horizontalResizer.y = dividerY * viewContainer.height - horizontalResizer.grabSize
}
function resetDividers()
{
dividerX = activePreset === "3Left1Right" ? 0.25 : 0.5
dividerY = 0.5
verticalResizer.x = activeDividers[activePreset].x * viewContainer.width - verticalResizer.grabSize
horizontalResizer.y = activeDividers[activePreset].y * viewContainer.height - horizontalResizer.grabSize
}
// Update viewports based on selected preset
@@ -742,23 +742,20 @@ Item {
if (!preset)
return;
let count = preset.numViewports;
if (activeViewport >= count)
if (activeViewport >= preset.length)
activeViewport = 0;
for (let i = 0; i < 4; ++i) {
if (i < count) {
if (i < preset.length) {
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;
viewRects[i].x = preset[i].x * viewContainer.width;
viewRects[i].y = preset[i].y * viewContainer.height;
viewRects[i].width = preset[i].width * viewContainer.width;
viewRects[i].height = preset[i].height * viewContainer.height;
} else {
viewRects[i].visible = false;
}
}
resetDividers();
updateViewRects();
updateSplitResizers();
@@ -769,35 +766,18 @@ Item {
// viewport preset and resizer(s) position.
function updateViewRects()
{
var w = viewContainer.width, h = viewContainer.height
var w = viewContainer.width
var h = viewContainer.height
var dividerX = activeDividers[activePreset].x
var dividerY = activeDividers[activePreset].y
switch (activePreset) {
case "Single":
case EditView3D.ViewPreset.Single:
viewRect0.width = w;
viewRect0.height = h;
break;
case "2Vertical":
viewRect0.width = dividerX * w;
viewRect0.height = h;
viewRect1.x = dividerX * w;
viewRect1.width = (1 - dividerX) * w;
viewRect1.height = h;
splitBorderV.x = dividerX * w;
break;
case "2Horizontal":
viewRect0.width = w;
viewRect0.height = dividerY * h;
viewRect1.y = dividerY * h;
viewRect1.width = w;
viewRect1.height = (1 - dividerY) * h;
splitBorderH1.y = dividerY * h;
splitBorderH1.width = w;
break;
case "Quad":
case EditView3D.ViewPreset.Quad:
// topleft
viewRect0.width = dividerX * w;
viewRect0.height = dividerY * h;
@@ -820,7 +800,7 @@ Item {
splitBorderH1.width = w;
break;
case "3Left1Right":
case EditView3D.ViewPreset.ThreeLeftOneRight:
// big right view
viewRect0.x = dividerX * w;
viewRect0.width = (1 - dividerX) * w;
@@ -843,12 +823,44 @@ Item {
splitBorderH1.width = dividerX * w;
splitBorderH2.y = dividerY2 * h;
break;
case EditView3D.ViewPreset.TwoHorizontal:
viewRect0.width = w;
viewRect0.height = dividerY * h;
viewRect1.y = dividerY * h;
viewRect1.width = w;
viewRect1.height = (1 - dividerY) * h;
splitBorderH1.y = dividerY * h;
splitBorderH1.width = w;
break;
case EditView3D.ViewPreset.TwoVertical:
viewRect0.width = dividerX * w;
viewRect0.height = h;
viewRect1.x = dividerX * w;
viewRect1.width = (1 - dividerX) * w;
viewRect1.height = h;
splitBorderV.x = dividerX * w;
break;
}
// Request overlays to redraw
_generalHelper.requestOverlayUpdate();
}
function defaultDividers() {
return [
{ x: 0.0, y: 0.0 },
{ x: 0.5, y: 0.5 },
{ x: 0.25, y: 0.0 },
{ x: 0.0, y: 0.5 },
{ x: 0.5, y: 0.0 }
]
}
function updateMouseCursor()
{
if (verticalResizer.containsMouse || verticalResizer.dragActive)
@@ -1038,9 +1050,9 @@ Item {
Rectangle {
id: splitBorderV
visible: viewRoot.activePreset === "2Vertical"
|| viewRoot.activePreset === "3Left1Right"
|| viewRoot.activePreset === "Quad"
visible: viewRoot.activePreset === EditView3D.ViewPreset.TwoVertical
|| viewRoot.activePreset === EditView3D.ViewPreset.ThreeLeftOneRight
|| viewRoot.activePreset === EditView3D.ViewPreset.Quad
y: 0
width: 1
height: parent.height
@@ -1050,9 +1062,9 @@ Item {
Rectangle {
id: splitBorderH1
visible: viewRoot.activePreset === "2Horizontal"
|| viewRoot.activePreset === "3Left1Right"
|| viewRoot.activePreset === "Quad"
visible: viewRoot.activePreset === EditView3D.ViewPreset.TwoHorizontal
|| viewRoot.activePreset === EditView3D.ViewPreset.ThreeLeftOneRight
|| viewRoot.activePreset === EditView3D.ViewPreset.Quad
x: 0
height: 1
border.width: 1
@@ -1061,7 +1073,7 @@ Item {
Rectangle {
id: splitBorderH2
visible: viewRoot.activePreset === "3Left1Right"
visible: viewRoot.activePreset === EditView3D.ViewPreset.ThreeLeftOneRight
x: 0
height: 1
width: splitBorderH1.width
@@ -1071,7 +1083,8 @@ Item {
// Active viewport highlight
Rectangle {
visible: activePreset !== "Single" && viewRects[viewRoot.activeViewport].visible
visible: activePreset !== EditView3D.ViewPreset.Single
&& viewRects[viewRoot.activeViewport].visible
x: viewRects[viewRoot.activeViewport].x
y: viewRects[viewRoot.activeViewport].y
width: viewRects[viewRoot.activeViewport].width
@@ -1086,19 +1099,26 @@ Item {
ViewportResizer {
id: verticalResizer
orientation: Qt.Vertical
divider: dividerX
containerSize: viewContainer.width
y: 0
height: viewContainer.height
visible: viewRoot.activePreset === "2Vertical"
|| viewRoot.activePreset === "3Left1Right"
|| viewRoot.activePreset === "Quad"
visible: viewRoot.activePreset === EditView3D.ViewPreset.TwoVertical
|| viewRoot.activePreset === EditView3D.ViewPreset.ThreeLeftOneRight
|| viewRoot.activePreset === EditView3D.ViewPreset.Quad
onCurrentDividerChanged: (value) => {
dividerX = value;
viewRoot.activeDividers[viewRoot.activePreset]
= { x: value,
y: viewRoot.activeDividers[viewRoot.activePreset].y};
updateViewRects();
}
onContainsMouseChanged: viewRoot.updateMouseCursor()
onDragActiveChanged: viewRoot.updateMouseCursor()
onDragActiveChanged: {
viewRoot.updateMouseCursor()
if (!dragActive) {
_generalHelper.storeToolState(viewRoot.sceneId, "activeDividers",
viewRoot.activeDividers)
}
}
}
// Horizontal divider (top/bottom)
@@ -1106,18 +1126,25 @@ Item {
id: horizontalResizer
orientation: Qt.Horizontal
divider: dividerY
containerSize: viewContainer.height
x: 0
width: viewContainer.width
visible: viewRoot.activePreset === "2Horizontal"
|| viewRoot.activePreset === "Quad"
visible: viewRoot.activePreset === EditView3D.ViewPreset.TwoHorizontal
|| viewRoot.activePreset === EditView3D.ViewPreset.Quad
onCurrentDividerChanged: (value) => {
dividerY = value;
viewRoot.activeDividers[viewRoot.activePreset]
= { x: viewRoot.activeDividers[viewRoot.activePreset].x,
y: value};
updateViewRects();
}
onContainsMouseChanged: viewRoot.updateMouseCursor()
onDragActiveChanged: viewRoot.updateMouseCursor()
onDragActiveChanged: {
viewRoot.updateMouseCursor()
if (!dragActive) {
_generalHelper.storeToolState(viewRoot.sceneId, "activeDividers",
viewRoot.activeDividers)
}
}
}
MouseArea {
@@ -1340,21 +1367,21 @@ Item {
function updateSnapping() {
switch (viewRoot.activePreset) {
case "Single":
case EditView3D.ViewPreset.Single:
cameraView.snapLeft = true
break
case "2Vertical":
cameraView.snapLeft = viewRoot.activeViewport == 1
break
case "2Horizontal":
cameraView.snapLeft = true
break
case "Quad":
case EditView3D.ViewPreset.Quad:
cameraView.snapLeft = viewRoot.activeViewport != 2
break
case "3Left1Right":
case EditView3D.ViewPreset.ThreeLeftOneRight:
cameraView.snapLeft = false
break
case EditView3D.ViewPreset.TwoHorizontal:
cameraView.snapLeft = true
break
case EditView3D.ViewPreset.TwoVertical:
cameraView.snapLeft = viewRoot.activeViewport == 1
break
}
}
}

View File

@@ -7,7 +7,6 @@ import QtQuick
Item {
id: root
property real divider
property real containerSize
property int orientation
readonly property alias containsMouse: mouseArea.containsMouse