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

View File

@@ -33,27 +33,24 @@ import StudioTheme 1.0 as StudioTheme
FocusScope {
id: root
property int delegateTopAreaHeight: StudioTheme.Values.height + 8
property int delegateBottomAreaHeight: delegateHeight - 2 * delegateStateMargin - delegateTopAreaHeight - delegateColumnSpacing
property int delegateColumnSpacing: 2
property int delegateStateMargin: 16
property int delegatePreviewMargin: 10
property int effectiveHeight: root.expanded ? Math.max(85, Math.min(287, root.height)) : 85 // height of the states area
readonly property int delegateTopAreaHeight: StudioTheme.Values.height + 8
readonly property int delegateBottomAreaHeight: delegateHeight - 2 * delegateStateMargin - delegateTopAreaHeight - 2
readonly property int delegateStateMargin: 16
readonly property int delegatePreviewMargin: 10
readonly property int effectiveHeight: root.height < 130 ? 89 : Math.min(root.height, 287)
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 deleteState(int internalNodeId)
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 {
target: statesEditorModel
function onChangedToState(n) { root.currentStateInternalId = n }
@@ -65,69 +62,32 @@ FocusScope {
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 {
id: addStateButton
buttonIcon: root.expanded ? qsTr("Create New State") : StudioTheme.Constants.plus
iconFont: root.expanded ? StudioTheme.Constants.font : StudioTheme.Constants.iconFont
iconSize: root.expanded ? StudioTheme.Values.myFontSize : StudioTheme.Values.myIconFontSize
iconItalic: root.expanded
buttonIcon: StudioTheme.Constants.plus
iconFont: StudioTheme.Constants.iconFont
iconSize: StudioTheme.Values.myIconFontSize
tooltip: qsTr("Add a new state.")
visible: canAddNewStates
anchors.right: parent.right
anchors.rightMargin: 8
y: (Math.min(effectiveHeight, root.height) - height) / 2
width: root.expanded ? 140 : 18
height: root.expanded ? 60 : 18
anchors.rightMargin: 4
anchors.bottom: parent.bottom
anchors.bottomMargin: 4 + scrollBarH
width: 30
height: 30
onClicked: {
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
onClicked: root.createNewState()
}
ListView {
id: statesListView
boundsBehavior: Flickable.StopAtBounds
clip: true
x: root.padding
y: root.padding
width: Math.min(root.delegateWidth * statesListView.count + root.innerSpacing * (statesListView.count - 1),
root.width - addStateButton.width - root.padding - 16) // 16 = 2 * 8 (addStateButton margin)
height: root.delegateHeight + StudioTheme.Values.scrollBarThickness
anchors.fill: parent
anchors.topMargin: listMargin
anchors.leftMargin: listMargin
anchors.rightMargin: listMargin
model: statesEditorModel
orientation: ListView.Horizontal
@@ -139,20 +99,18 @@ FocusScope {
height: root.delegateHeight
isBaseState: 0 === internalNodeId
isCurrentState: root.currentStateInternalId === internalNodeId
baseColor: isCurrentState ? StudioTheme.Values.themeInteraction : background.color
delegateStateName: stateName
delegateStateImageSource: stateImageSource
delegateHasWhenCondition: hasWhenCondition
delegateWhenConditionString: whenConditionString
onDelegateInteraction: contextMenu.dismiss()
columnSpacing: root.delegateColumnSpacing
topAreaHeight: root.delegateTopAreaHeight
bottomAreaHeight: root.delegateBottomAreaHeight
stateMargin: root.delegateStateMargin
previewMargin: root.delegatePreviewMargin
scrollBarH: root.scrollBarH
listMargin: root.listMargin
}
ScrollBar.horizontal: HorizontalScrollBar {}
}
}

View File

@@ -85,12 +85,6 @@ void StatesEditorView::rootNodeTypeChanged(const QString &/*type*/, int /*majorV
checkForStatesAvailability();
}
void StatesEditorView::toggleStatesViewExpanded()
{
if (m_statesEditorWidget)
m_statesEditorWidget->toggleStatesViewExpanded();
}
void StatesEditorView::removeState(int nodeId)
{
try {
@@ -102,6 +96,22 @@ void StatesEditorView::removeState(int nodeId)
if (modelState.isValid()) {
QStringList lockedTargets;
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) {
const ModelNode target = change.target();
QTC_ASSERT(target.isValid(), continue);

View File

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

View File

@@ -133,13 +133,6 @@ QString StatesEditorWidget::qmlSourcesPath()
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)
{
QQuickWidget::showEvent(event);
@@ -168,18 +161,6 @@ void StatesEditorWidget::reloadQmlSource()
connect(rootObject(), SIGNAL(createNewState()), m_statesEditorView.data(), SLOT(createNewState()));
connect(rootObject(), SIGNAL(deleteState(int)), m_statesEditorView.data(), SLOT(removeState(int)));
m_statesEditorView.data()->synchonizeCurrentStateFromWidget();
if (!DesignerSettings::getValue(DesignerSettingsKey::STATESEDITOR_EXPANDED).toBool())
toggleStatesViewExpanded();
connect(rootObject(), SIGNAL(expandedChanged()), this, SLOT(handleExpandedChanged()));
}
void StatesEditorWidget::handleExpandedChanged()
{
QTC_ASSERT(rootObject(), return);
bool expanded = rootObject()->property("expanded").toBool();
DesignerSettings::setValue(DesignerSettingsKey::STATESEDITOR_EXPANDED, expanded);
}
}
} // QmlDesigner

View File

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

View File

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

View File

@@ -405,11 +405,6 @@ const DesignerActionManager &ViewManager::designerActionManager() const
return d->designerActionManagerView.designerActionManager();
}
void ViewManager::toggleStatesViewExpanded()
{
d->statesEditorView.toggleStatesViewExpanded();
}
void ViewManager::qmlJSEditorContextHelp(const Core::IContext::HelpCallback &callback) const
{
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::REFORMAT_UI_QML_FILES, true);
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_REVERSE_ITEM_ORDER, 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 DEBUG_PUPPET[] = "DebugPuppet";
const char FORWARD_PUPPET_OUTPUT[] = "ForwardPuppetOutput";
const char STATESEDITOR_EXPANDED[] = "StatesEditorExpanded";
const char NAVIGATOR_SHOW_ONLY_VISIBLE_ITEMS[] = "NavigatorShowOnlyVisibleItems";
const char NAVIGATOR_REVERSE_ITEM_ORDER[] = "NavigatorReverseItemOrder";
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_pasteAction(tr("&Paste")),
m_selectAllAction(tr("Select &All")),
m_collapseExpandStatesAction(tr("Toggle States")),
m_escapeAction(this)
{
@@ -97,10 +96,6 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex
connect(&m_selectAllAction,&QAction::triggered, this, &ShortCutManager::selectAll);
connect(&m_collapseExpandStatesAction, &QAction::triggered, [] {
QmlDesignerPlugin::instance()->viewManager().toggleStatesViewExpanded();
});
// Revert to saved
Core::EditorManager *em = Core::EditorManager::instance();
Core::ActionManager::registerAction(&m_revertToSavedAction,Core::Constants::REVERTTOSAVED, qmlDesignerMainContext);
@@ -188,13 +183,6 @@ void ShortCutManager::registerActions(const Core::Context &qmlDesignerMainContex
command->setDefaultKeySequence(QKeySequence::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. */
command = Core::ActionManager::registerAction(&m_escapeAction, Core::Constants::S_RETURNTOEDITOR, qmlDesignerMainContext);
command->setDefaultKeySequence(QKeySequence(Qt::Key_Escape));

View File

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