QmlDesigner: Add some states view improvements

- Added a gray border around unselected states.
- States resize to fit view height.
- States are centered vertically in the view.
- Removed collapse option, auto collapse when space is small.
- scroll bar always at the bottom.
- Overshoot list ends.
- Added margins around the states.
- Add states button: make it small and docked to the bottom right.
- Add states button doesnt take space from the view.

Change-Id: I4fc96f4341a6e4a0c70509240b7aed9c7890ec4d
Reviewed-by: Thomas Hartmann <thomas.hartmann@qt.io>
Reviewed-by: Miikka Heikkinen <miikka.heikkinen@qt.io>
Reviewed-by: Samuel Ghinet <samuel.ghinet@qt.io>
Reviewed-by: <github-actions-qt-creator@cristianadam.eu>
This commit is contained in:
Mahmoud Badri
2021-12-15 11:56:24 +02:00
parent 6073f3396c
commit 0d3ef2489d
12 changed files with 54 additions and 133 deletions

View File

@@ -33,11 +33,8 @@ import StudioTheme 1.0 as StudioTheme
Rectangle { Rectangle {
id: myRoot id: myRoot
color: baseColor
property bool isBaseState property bool isBaseState
property bool isCurrentState property bool isCurrentState
property color baseColor
property string delegateStateName property string delegateStateName
property string delegateStateImageSource property string delegateStateImageSource
property bool delegateHasWhenCondition property bool delegateHasWhenCondition
@@ -47,14 +44,14 @@ Rectangle {
property int bottomAreaHeight property int bottomAreaHeight
property int stateMargin property int stateMargin
property int previewMargin property int previewMargin
property int columnSpacing
readonly property bool isDefaultState: isDefault readonly property bool isDefaultState: isDefault
property int closeButtonMargin: 6 property int closeButtonMargin: 6
property int textFieldMargin: 4 property int textFieldMargin: 4
signal delegateInteraction property int scrollBarH: 0
property int listMargin: 0
function autoComplete(text, pos, explicitComplete, filter) { function autoComplete(text, pos, explicitComplete, filter) {
var stringList = statesEditorModel.autoComplete(text, pos, explicitComplete) var stringList = statesEditorModel.autoComplete(text, pos, explicitComplete)
@@ -65,14 +62,19 @@ Rectangle {
return statesEditorModel.hasAnnotation(internalNodeId) return statesEditorModel.hasAnnotation(internalNodeId)
} }
color: isCurrentState ? StudioTheme.Values.themeInteraction
: StudioTheme.Values.themeControlBackgroundInteraction
anchors.verticalCenter: parent.verticalCenter
anchors.verticalCenterOffset: -.5 * (scrollBarH + listMargin)
MouseArea { MouseArea {
id: mouseArea id: mouseArea
anchors.fill: parent anchors.fill: parent
onClicked: { onClicked: {
focus = true focus = true
root.currentStateInternalId = internalNodeId root.currentStateInternalId = internalNodeId
contextMenu.dismiss() // close potentially open context menu contextMenu.dismiss() // close potentially open context menu
myRoot.delegateInteraction()
} }
} }
@@ -89,7 +91,6 @@ Rectangle {
visible: !isBaseState && isCurrentState visible: !isBaseState && isCurrentState
onClicked: { onClicked: {
myRoot.delegateInteraction()
if (isDefaultState) if (isDefaultState)
statesEditorModel.resetDefaultState() statesEditorModel.resetDefaultState()
@@ -254,9 +255,8 @@ Rectangle {
Rectangle { // separator Rectangle { // separator
width: column.width width: column.width
height: myRoot.columnSpacing height: 2
color: StudioTheme.Values.themeStateSeparator color: StudioTheme.Values.themeStateSeparator
visible: expanded
} }
Rectangle { Rectangle {
@@ -264,7 +264,6 @@ Rectangle {
width: myRoot.width - 2 * myRoot.stateMargin width: myRoot.width - 2 * myRoot.stateMargin
height: myRoot.bottomAreaHeight height: myRoot.bottomAreaHeight
color: StudioTheme.Values.themeStateBackground color: StudioTheme.Values.themeStateBackground
visible: expanded
Image { Image {
anchors.fill: stateImageBackground anchors.fill: stateImageBackground

View File

@@ -33,27 +33,24 @@ import StudioTheme 1.0 as StudioTheme
FocusScope { FocusScope {
id: root id: root
property int delegateTopAreaHeight: StudioTheme.Values.height + 8 readonly property int delegateTopAreaHeight: StudioTheme.Values.height + 8
property int delegateBottomAreaHeight: delegateHeight - 2 * delegateStateMargin - delegateTopAreaHeight - delegateColumnSpacing readonly property int delegateBottomAreaHeight: delegateHeight - 2 * delegateStateMargin - delegateTopAreaHeight - 2
property int delegateColumnSpacing: 2 readonly property int delegateStateMargin: 16
property int delegateStateMargin: 16 readonly property int delegatePreviewMargin: 10
property int delegatePreviewMargin: 10 readonly property int effectiveHeight: root.height < 130 ? 89 : Math.min(root.height, 287)
property int effectiveHeight: root.expanded ? Math.max(85, Math.min(287, root.height)) : 85 // height of the states area
readonly property int scrollBarH: statesListView.ScrollBar.horizontal.scrollBarVisible ? StudioTheme.Values.scrollBarThickness : 0
readonly property int listMargin: 10
readonly property int delegateWidth: 264
readonly property int delegateHeight: Math.max(effectiveHeight - scrollBarH - 2 * listMargin, 69)
readonly property int innerSpacing: 2
property int currentStateInternalId: 0
signal createNewState signal createNewState
signal deleteState(int internalNodeId) signal deleteState(int internalNodeId)
signal duplicateCurrentState signal duplicateCurrentState
property int padding: 2
property int delegateWidth: 264
property int delegateHeight: effectiveHeight
- StudioTheme.Values.scrollBarThickness
- 2 * (root.padding + StudioTheme.Values.border)
property int innerSpacing: 2
property int currentStateInternalId: 0
property bool expanded: true
Connections { Connections {
target: statesEditorModel target: statesEditorModel
function onChangedToState(n) { root.currentStateInternalId = n } function onChangedToState(n) { root.currentStateInternalId = n }
@@ -65,69 +62,32 @@ FocusScope {
color: StudioTheme.Values.themePanelBackground color: StudioTheme.Values.themePanelBackground
} }
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.LeftButton | Qt.RightButton
onClicked: function(mouse) {
if (mouse.button === Qt.LeftButton) {
contextMenu.dismiss()
focus = true
} else if (mouse.button === Qt.RightButton) {
contextMenu.popup()
}
}
StudioControls.Menu {
id: contextMenu
StudioControls.MenuItem {
text: root.expanded ? qsTr("Collapse") : qsTr("Expand")
onTriggered: root.expanded = !root.expanded
}
}
}
AbstractButton { AbstractButton {
id: addStateButton id: addStateButton
buttonIcon: root.expanded ? qsTr("Create New State") : StudioTheme.Constants.plus buttonIcon: StudioTheme.Constants.plus
iconFont: root.expanded ? StudioTheme.Constants.font : StudioTheme.Constants.iconFont iconFont: StudioTheme.Constants.iconFont
iconSize: root.expanded ? StudioTheme.Values.myFontSize : StudioTheme.Values.myIconFontSize iconSize: StudioTheme.Values.myIconFontSize
iconItalic: root.expanded
tooltip: qsTr("Add a new state.") tooltip: qsTr("Add a new state.")
visible: canAddNewStates visible: canAddNewStates
anchors.right: parent.right anchors.right: parent.right
anchors.rightMargin: 8 anchors.rightMargin: 4
y: (Math.min(effectiveHeight, root.height) - height) / 2 anchors.bottom: parent.bottom
width: root.expanded ? 140 : 18 anchors.bottomMargin: 4 + scrollBarH
height: root.expanded ? 60 : 18 width: 30
height: 30
onClicked: { onClicked: root.createNewState()
contextMenu.dismiss()
root.createNewState()
}
}
Rectangle { // separator lines between state items
color: StudioTheme.Values.themeStateSeparator
x: root.padding
y: root.padding
width: statesListView.width
height: root.delegateHeight
} }
ListView { ListView {
id: statesListView id: statesListView
boundsBehavior: Flickable.StopAtBounds
clip: true clip: true
anchors.fill: parent
x: root.padding anchors.topMargin: listMargin
y: root.padding anchors.leftMargin: listMargin
width: Math.min(root.delegateWidth * statesListView.count + root.innerSpacing * (statesListView.count - 1), anchors.rightMargin: listMargin
root.width - addStateButton.width - root.padding - 16) // 16 = 2 * 8 (addStateButton margin)
height: root.delegateHeight + StudioTheme.Values.scrollBarThickness
model: statesEditorModel model: statesEditorModel
orientation: ListView.Horizontal orientation: ListView.Horizontal
@@ -139,20 +99,18 @@ FocusScope {
height: root.delegateHeight height: root.delegateHeight
isBaseState: 0 === internalNodeId isBaseState: 0 === internalNodeId
isCurrentState: root.currentStateInternalId === internalNodeId isCurrentState: root.currentStateInternalId === internalNodeId
baseColor: isCurrentState ? StudioTheme.Values.themeInteraction : background.color
delegateStateName: stateName delegateStateName: stateName
delegateStateImageSource: stateImageSource delegateStateImageSource: stateImageSource
delegateHasWhenCondition: hasWhenCondition delegateHasWhenCondition: hasWhenCondition
delegateWhenConditionString: whenConditionString delegateWhenConditionString: whenConditionString
onDelegateInteraction: contextMenu.dismiss()
columnSpacing: root.delegateColumnSpacing
topAreaHeight: root.delegateTopAreaHeight topAreaHeight: root.delegateTopAreaHeight
bottomAreaHeight: root.delegateBottomAreaHeight bottomAreaHeight: root.delegateBottomAreaHeight
stateMargin: root.delegateStateMargin stateMargin: root.delegateStateMargin
previewMargin: root.delegatePreviewMargin previewMargin: root.delegatePreviewMargin
scrollBarH: root.scrollBarH
listMargin: root.listMargin
} }
ScrollBar.horizontal: HorizontalScrollBar {} ScrollBar.horizontal: HorizontalScrollBar {}
} }
} }

View File

@@ -85,12 +85,6 @@ void StatesEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorV
checkForStatesAvailability(); checkForStatesAvailability();
} }
void StatesEditorView::toggleStatesViewExpanded()
{
if (m_statesEditorWidget)
m_statesEditorWidget->toggleStatesViewExpanded();
}
void StatesEditorView::removeState(int nodeId) void StatesEditorView::removeState(int nodeId)
{ {
try { try {
@@ -102,6 +96,22 @@ void StatesEditorView::removeState(int nodeId)
if (modelState.isValid()) { if (modelState.isValid()) {
QStringList lockedTargets; QStringList lockedTargets;
const auto propertyChanges = modelState.propertyChanges(); const auto propertyChanges = modelState.propertyChanges();
// confirm removing not empty states
if (!propertyChanges.isEmpty()) {
QMessageBox msgBox;
msgBox.setTextFormat(Qt::RichText);
msgBox.setIcon(QMessageBox::Question);
msgBox.setWindowTitle(tr("Remove State"));
msgBox.setText(tr("This state is not empty. Are you sure you want to remove it?"));
msgBox.setStandardButtons(QMessageBox::Yes | QMessageBox::Cancel);
msgBox.setDefaultButton(QMessageBox::Yes);
if (msgBox.exec() == QMessageBox::Cancel)
return;
}
// confirm removing states with locked targets
for (const QmlPropertyChanges &change : propertyChanges) { for (const QmlPropertyChanges &change : propertyChanges) {
const ModelNode target = change.target(); const ModelNode target = change.target();
QTC_ASSERT(target.isValid(), continue); QTC_ASSERT(target.isValid(), continue);

View File

@@ -87,8 +87,6 @@ public:
void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override; void rootNodeTypeChanged(const QString &type, int majorVersion, int minorVersion) override;
void toggleStatesViewExpanded();
public slots: public slots:
void synchonizeCurrentStateFromWidget(); void synchonizeCurrentStateFromWidget();
void createNewState(); void createNewState();

View File

@@ -133,13 +133,6 @@ QString StatesEditorWidget::qmlSourcesPath()
return Core::ICore::resourcePath("qmldesigner/statesEditorQmlSources").toString(); return Core::ICore::resourcePath("qmldesigner/statesEditorQmlSources").toString();
} }
void StatesEditorWidget::toggleStatesViewExpanded()
{
QTC_ASSERT(rootObject(), return);
bool expanded = rootObject()->property("expanded").toBool();
rootObject()->setProperty("expanded", !expanded);
}
void StatesEditorWidget::showEvent(QShowEvent *event) void StatesEditorWidget::showEvent(QShowEvent *event)
{ {
QQuickWidget::showEvent(event); QQuickWidget::showEvent(event);
@@ -168,18 +161,6 @@ void StatesEditorWidget::reloadQmlSource()
connect(rootObject(), SIGNAL(createNewState()), m_statesEditorView.data(), SLOT(createNewState())); connect(rootObject(), SIGNAL(createNewState()), m_statesEditorView.data(), SLOT(createNewState()));
connect(rootObject(), SIGNAL(deleteState(int)), m_statesEditorView.data(), SLOT(removeState(int))); connect(rootObject(), SIGNAL(deleteState(int)), m_statesEditorView.data(), SLOT(removeState(int)));
m_statesEditorView.data()->synchonizeCurrentStateFromWidget(); m_statesEditorView.data()->synchonizeCurrentStateFromWidget();
if (!DesignerSettings::getValue(DesignerSettingsKey::STATESEDITOR_EXPANDED).toBool())
toggleStatesViewExpanded();
connect(rootObject(), SIGNAL(expandedChanged()), this, SLOT(handleExpandedChanged()));
} }
void StatesEditorWidget::handleExpandedChanged() } // QmlDesigner
{
QTC_ASSERT(rootObject(), return);
bool expanded = rootObject()->property("expanded").toBool();
DesignerSettings::setValue(DesignerSettingsKey::STATESEDITOR_EXPANDED, expanded);
}
}

View File

@@ -57,14 +57,11 @@ public:
static QString qmlSourcesPath(); static QString qmlSourcesPath();
void toggleStatesViewExpanded();
protected: protected:
void showEvent(QShowEvent *) override; void showEvent(QShowEvent *) override;
private: private:
void reloadQmlSource(); void reloadQmlSource();
Q_SLOT void handleExpandedChanged();
private: private:
QPointer<StatesEditorView> m_statesEditorView; QPointer<StatesEditorView> m_statesEditorView;

View File

@@ -94,8 +94,6 @@ public:
DesignerActionManager &designerActionManager(); DesignerActionManager &designerActionManager();
const DesignerActionManager &designerActionManager() const; const DesignerActionManager &designerActionManager() const;
void toggleStatesViewExpanded();
void qmlJSEditorContextHelp(const Core::IContext::HelpCallback &callback) const; void qmlJSEditorContextHelp(const Core::IContext::HelpCallback &callback) const;
DesignDocument *currentDesignDocument() const; DesignDocument *currentDesignDocument() const;

View File

@@ -405,11 +405,6 @@ const DesignerActionManager &ViewManager::designerActionManager() const
return d->designerActionManagerView.designerActionManager(); return d->designerActionManagerView.designerActionManager();
} }
void ViewManager::toggleStatesViewExpanded()
{
d->statesEditorView.toggleStatesViewExpanded();
}
void ViewManager::qmlJSEditorContextHelp(const Core::IContext::HelpCallback &callback) const void ViewManager::qmlJSEditorContextHelp(const Core::IContext::HelpCallback &callback) const
{ {
d->textEditorView.qmlJSEditorContextHelp(callback); d->textEditorView.qmlJSEditorContextHelp(callback);

View File

@@ -72,7 +72,6 @@ void DesignerSettings::fromSettings(QSettings *settings)
restoreValue(settings, DesignerSettingsKey::FORWARD_PUPPET_OUTPUT, QString()); restoreValue(settings, DesignerSettingsKey::FORWARD_PUPPET_OUTPUT, QString());
restoreValue(settings, DesignerSettingsKey::REFORMAT_UI_QML_FILES, true); restoreValue(settings, DesignerSettingsKey::REFORMAT_UI_QML_FILES, true);
restoreValue(settings, DesignerSettingsKey::IGNORE_DEVICE_PIXEL_RATIO, false); restoreValue(settings, DesignerSettingsKey::IGNORE_DEVICE_PIXEL_RATIO, false);
restoreValue(settings, DesignerSettingsKey::STATESEDITOR_EXPANDED, true);
restoreValue(settings, DesignerSettingsKey::NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS, true); restoreValue(settings, DesignerSettingsKey::NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS, true);
restoreValue(settings, DesignerSettingsKey::NAVIGATOR_REVERSE_ITEM_ORDER, false); restoreValue(settings, DesignerSettingsKey::NAVIGATOR_REVERSE_ITEM_ORDER, false);
restoreValue(settings, DesignerSettingsKey::STANDALONE_MODE, false); restoreValue(settings, DesignerSettingsKey::STANDALONE_MODE, false);

View File

@@ -60,7 +60,6 @@ const char ENABLE_MODEL_EXCEPTION_OUTPUT[] = "WarnException";
const char PUPPET_KILL_TIMEOUT[] = "PuppetKillTimeout"; const char PUPPET_KILL_TIMEOUT[] = "PuppetKillTimeout";
const char DEBUG_PUPPET[] = "DebugPuppet"; const char DEBUG_PUPPET[] = "DebugPuppet";
const char FORWARD_PUPPET_OUTPUT[] = "ForwardPuppetOutput"; const char FORWARD_PUPPET_OUTPUT[] = "ForwardPuppetOutput";
const char STATESEDITOR_EXPANDED[] = "StatesEditorExpanded";
const char NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS[] = "NavigatorShowOnlyVisibleItems"; const char NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS[] = "NavigatorShowOnlyVisibleItems";
const char NAVIGATOR_REVERSE_ITEM_ORDER[] = "NavigatorReverseItemOrder"; const char NAVIGATOR_REVERSE_ITEM_ORDER[] = "NavigatorReverseItemOrder";
const char REFORMAT_UI_QML_FILES[] = "ReformatUiQmlFiles"; /* These settings are not exposed in ui. */ const char REFORMAT_UI_QML_FILES[] = "ReformatUiQmlFiles"; /* These settings are not exposed in ui. */

View File

@@ -69,7 +69,6 @@ ShortCutManager::ShortCutManager()
m_copyAction(tr("&Copy")), m_copyAction(tr("&Copy")),
m_pasteAction(tr("&Paste")), m_pasteAction(tr("&Paste")),
m_selectAllAction(tr("Select &All")), m_selectAllAction(tr("Select &All")),
m_collapseExpandStatesAction(tr("Toggle States")),
m_escapeAction(this) m_escapeAction(this)
{ {
@@ -97,10 +96,6 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex
connect(&m_selectAllAction,&QAction::triggered, this, &ShortCutManager::selectAll); connect(&m_selectAllAction,&QAction::triggered, this, &ShortCutManager::selectAll);
connect(&m_collapseExpandStatesAction, &QAction::triggered, [] {
QmlDesignerPlugin::instance()->viewManager().toggleStatesViewExpanded();
});
// Revert to saved // Revert to saved
Core::EditorManager *em = Core::EditorManager::instance(); Core::EditorManager *em = Core::EditorManager::instance();
Core::ActionManager::registerAction(&m_revertToSavedAction,Core::Constants::REVERTTOSAVED, qmlDesignerMainContext); Core::ActionManager::registerAction(&m_revertToSavedAction,Core::Constants::REVERTTOSAVED, qmlDesignerMainContext);
@@ -188,13 +183,6 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex
command->setDefaultKeySequence(QKeySequence::SelectAll); command->setDefaultKeySequence(QKeySequence::SelectAll);
editMenu->addAction(command, Core::Constants::G_EDIT_SELECTALL); editMenu->addAction(command, Core::Constants::G_EDIT_SELECTALL);
Core::ActionContainer *viewsMenu = Core::ActionManager::actionContainer(Core::Constants::M_VIEW_VIEWS);
command = Core::ActionManager::registerAction(&m_collapseExpandStatesAction, Constants::TOGGLE_STATES_EDITOR, qmlDesignerMainContext);
command->setAttribute(Core::Command::CA_Hide);
command->setDefaultKeySequence(QKeySequence("Ctrl+Alt+s"));
viewsMenu->addAction(command);
/* Registering disabled action for Escape, because Qt Quick does not support shortcut overrides. */ /* Registering disabled action for Escape, because Qt Quick does not support shortcut overrides. */
command = Core::ActionManager::registerAction(&m_escapeAction, Core::Constants::S_RETURNTOEDITOR, qmlDesignerMainContext); command = Core::ActionManager::registerAction(&m_escapeAction, Core::Constants::S_RETURNTOEDITOR, qmlDesignerMainContext);
command->setDefaultKeySequence(QKeySequence(Qt::Key_Escape)); command->setDefaultKeySequence(QKeySequence(Qt::Key_Escape));

View File

@@ -83,7 +83,6 @@ private:
QAction m_copyAction; QAction m_copyAction;
QAction m_pasteAction; QAction m_pasteAction;
QAction m_selectAllAction; QAction m_selectAllAction;
QAction m_collapseExpandStatesAction;
QAction m_escapeAction; QAction m_escapeAction;
}; };