QmlDesigner/StateEditor: Improve adding new states

- Added a big add button at the end of the states list.
- Small add states button jumps in (bottom right) when the big button
is out of the view.
- View scrolls to the end when a new slide is added.

Task-number: QDS-5973
Change-Id: Ida96bd663cc0caf32889638fbf4ac9f617916368
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
This commit is contained in:
Mahmoud Badri
2022-01-21 13:34:16 +02:00
parent 0d3ef2489d
commit d5eab11359
4 changed files with 110 additions and 46 deletions

View File

@@ -64,9 +64,6 @@ Rectangle {
color: isCurrentState ? StudioTheme.Values.themeInteraction color: isCurrentState ? StudioTheme.Values.themeInteraction
: StudioTheme.Values.themeControlBackgroundInteraction : StudioTheme.Values.themeControlBackgroundInteraction
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: -.5 * (scrollBarH + listMargin)
MouseArea { MouseArea {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: parent

View File

@@ -26,6 +26,7 @@
import QtQuick 2.15 import QtQuick 2.15
import QtQuick.Controls 2.15 import QtQuick.Controls 2.15
import QtQuickDesignerTheme 1.0 import QtQuickDesignerTheme 1.0
import Qt.labs.qmlmodels 1.0
import HelperWidgets 2.0 import HelperWidgets 2.0
import StudioControls 1.0 as StudioControls import StudioControls 1.0 as StudioControls
import StudioTheme 1.0 as StudioTheme import StudioTheme 1.0 as StudioTheme
@@ -73,9 +74,16 @@ FocusScope {
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 4 anchors.rightMargin: 4
anchors.bottom: parent.bottom anchors.bottom: parent.bottom
anchors.bottomMargin: 4 + scrollBarH anchors.bottomMargin: statesListView.contentWidth - statesListView.contentX - root.delegateWidth / 2 > statesListView.width ? scrollBarH + 5 : -35
width: 30 width: 35
height: 30 height: 35
Behavior on anchors.bottomMargin {
PropertyAnimation {
duration: 700
easing.type: Easing.InOutBack
}
}
onClicked: root.createNewState() onClicked: root.createNewState()
} }
@@ -93,24 +101,81 @@ FocusScope {
orientation: ListView.Horizontal orientation: ListView.Horizontal
spacing: root.innerSpacing spacing: root.innerSpacing
delegate: StatesDelegate { property int prevCount: 0
id: statesDelegate onCountChanged: {
width: root.delegateWidth if (count > prevCount)
height: root.delegateHeight Qt.callLater(statesListView.positionViewAtEnd)
isBaseState: 0 === internalNodeId prevCount = count
isCurrentState: root.currentStateInternalId === internalNodeId
delegateStateName: stateName
delegateStateImageSource: stateImageSource
delegateHasWhenCondition: hasWhenCondition
delegateWhenConditionString: whenConditionString
topAreaHeight: root.delegateTopAreaHeight
bottomAreaHeight: root.delegateBottomAreaHeight
stateMargin: root.delegateStateMargin
previewMargin: root.delegatePreviewMargin
scrollBarH: root.scrollBarH
listMargin: root.listMargin
} }
delegate: DelegateChooser {
role: "type"
DelegateChoice {
roleValue: "state"
StatesDelegate {
width: root.delegateWidth
height: root.delegateHeight
anchors.verticalCenter: parent ? parent.verticalCenter : undefined
anchors.verticalCenterOffset: -.5 * (scrollBarH + listMargin)
isBaseState: 0 === internalNodeId
isCurrentState: root.currentStateInternalId === internalNodeId
delegateStateName: stateName
delegateStateImageSource: stateImageSource
delegateHasWhenCondition: hasWhenCondition
delegateWhenConditionString: whenConditionString
topAreaHeight: root.delegateTopAreaHeight
bottomAreaHeight: root.delegateBottomAreaHeight
stateMargin: root.delegateStateMargin
previewMargin: root.delegatePreviewMargin
scrollBarH: root.scrollBarH
listMargin: root.listMargin
}
}
DelegateChoice {
roleValue: "add"
Rectangle {
visible: canAddNewStates
width: root.delegateWidth
height: root.delegateHeight
anchors.verticalCenter: parent ? parent.verticalCenter : undefined
anchors.verticalCenterOffset: -.5 * (scrollBarH + listMargin)
color: Qt.lighter(StudioTheme.Values.themeControlBackgroundInteraction, addState.containsMouse ? 1.5 : 1)
ToolTip.text: qsTr("Add a new state.")
ToolTip.visible: addState.containsMouse
ToolTip.delay: 1000
Rectangle { // inner rect
width: parent.width - 30
height: parent.height - 30
anchors.centerIn: parent
color: StudioTheme.Values.themeStateBackground
}
Text {
text: "+"
anchors.centerIn: parent
anchors.verticalCenterOffset: -5
font.pixelSize: parent.height * .5
color: Qt.lighter(StudioTheme.Values.themeControlBackgroundInteraction, addState.containsMouse ? 1.5 : 1)
}
MouseArea {
id: addState
hoverEnabled: true
anchors.fill: parent
onClicked: root.createNewState()
}
}
}
}
ScrollBar.horizontal: HorizontalScrollBar {} ScrollBar.horizontal: HorizontalScrollBar {}
} }
} }

View File

@@ -53,7 +53,6 @@ StatesEditorModel::StatesEditorModel(StatesEditorView *view)
{ {
} }
int StatesEditorModel::count() const int StatesEditorModel::count() const
{ {
return rowCount(); return rowCount();
@@ -64,9 +63,8 @@ QModelIndex StatesEditorModel::index(int row, int column, const QModelIndex &par
if (m_statesEditorView.isNull()) if (m_statesEditorView.isNull())
return {}; return {};
int internalNodeId = 0; int internalNodeId = 0;
if (row > 0) if (row > 0 && row < rowCount() - 1) // first and last rows are base state, add state
internalNodeId = m_statesEditorView->rootModelNode().nodeListProperty("states").at(row - 1).internalId(); internalNodeId = m_statesEditorView->rootModelNode().nodeListProperty("states").at(row - 1).internalId();
return hasIndex(row, column, parent) ? createIndex(row, column, internalNodeId) : QModelIndex(); return hasIndex(row, column, parent) ? createIndex(row, column, internalNodeId) : QModelIndex();
@@ -78,9 +76,9 @@ int StatesEditorModel::rowCount(const QModelIndex &parent) const
return 0; return 0;
if (!m_statesEditorView->rootModelNode().hasNodeListProperty("states")) if (!m_statesEditorView->rootModelNode().hasNodeListProperty("states"))
return 1; return 2; // base state + add new state
return m_statesEditorView->rootModelNode().nodeListProperty("states").count() + 1; return m_statesEditorView->rootModelNode().nodeListProperty("states").count() + 2; // 2 = base state + add new state
} }
void StatesEditorModel::reset() void StatesEditorModel::reset()
@@ -101,16 +99,16 @@ QVariant StatesEditorModel::data(const QModelIndex &index, int role) const
switch (role) { switch (role) {
case StateNameRole: { case StateNameRole: {
if (index.row() == 0) { if (index.row() == 0) {
return tr("base state", "Implicit default state"); return tr("base state", "Implicit default state");
} else { } else {
if (stateNode.hasVariantProperty("name")) if (stateNode.hasVariantProperty("name"))
return stateNode.variantProperty("name").value(); return stateNode.variantProperty("name").value();
else else
return QVariant(); return QVariant();
}
} }
}
case StateImageSourceRole: { case StateImageSourceRole: {
static int randomNumber = 0; static int randomNumber = 0;
randomNumber++; randomNumber++;
@@ -119,9 +117,12 @@ QVariant StatesEditorModel::data(const QModelIndex &index, int role) const
else else
return QString("image://qmldesigner_stateseditor/%1-%2").arg(index.internalId()).arg(randomNumber); return QString("image://qmldesigner_stateseditor/%1-%2").arg(index.internalId()).arg(randomNumber);
} }
case InternalNodeId: return index.internalId();
case HasWhenCondition: return stateNode.isValid() && stateNode.hasProperty("when"); case InternalNodeId:
return index.internalId();
case HasWhenCondition:
return stateNode.isValid() && stateNode.hasProperty("when");
case WhenConditionString: { case WhenConditionString: {
if (stateNode.isValid() && stateNode.hasBindingProperty("when")) if (stateNode.isValid() && stateNode.hasBindingProperty("when"))
@@ -137,10 +138,11 @@ QVariant StatesEditorModel::data(const QModelIndex &index, int role) const
return false; return false;
} }
case ModelHasDefaultState: { case ModelHasDefaultState:
return hasDefaultState(); return hasDefaultState();
}
case StateType:
return index.row() == rowCount() - 1 ? "add" : "state";
} }
return QVariant(); return QVariant();
@@ -148,14 +150,15 @@ QVariant StatesEditorModel::data(const QModelIndex &index, int role) const
QHash<int, QByteArray> StatesEditorModel::roleNames() const QHash<int, QByteArray> StatesEditorModel::roleNames() const
{ {
static QHash<int, QByteArray> roleNames{ static QHash<int, QByteArray> roleNames {
{StateNameRole, "stateName"}, {StateNameRole, "stateName"},
{StateImageSourceRole, "stateImageSource"}, {StateImageSourceRole, "stateImageSource"},
{InternalNodeId, "internalNodeId"}, {InternalNodeId, "internalNodeId"},
{HasWhenCondition, "hasWhenCondition"}, {HasWhenCondition, "hasWhenCondition"},
{WhenConditionString, "whenConditionString"}, {WhenConditionString, "whenConditionString"},
{IsDefault, "isDefault"}, {IsDefault, "isDefault"},
{ModelHasDefaultState, "modelHasDefaultState"} {ModelHasDefaultState, "modelHasDefaultState"},
{StateType, "type"}
}; };
return roleNames; return roleNames;
} }
@@ -163,10 +166,8 @@ QHash<int, QByteArray> StatesEditorModel::roleNames() const
void StatesEditorModel::insertState(int stateIndex) void StatesEditorModel::insertState(int stateIndex)
{ {
if (stateIndex >= 0) { if (stateIndex >= 0) {
const int updateIndex = stateIndex + 1; const int updateIndex = stateIndex + 1;
beginInsertRows(QModelIndex(), updateIndex, updateIndex); beginInsertRows(QModelIndex(), updateIndex, updateIndex);
endInsertRows(); endInsertRows();
emit dataChanged(index(updateIndex, 0), index(updateIndex, 0)); emit dataChanged(index(updateIndex, 0), index(updateIndex, 0));

View File

@@ -44,7 +44,8 @@ class StatesEditorModel : public QAbstractListModel
HasWhenCondition, HasWhenCondition,
WhenConditionString, WhenConditionString,
IsDefault, IsDefault,
ModelHasDefaultState ModelHasDefaultState,
StateType
}; };
public: public: