Implement custom presets
Task-number: QDS-4989 Change-Id: I95844ae97204ad3bb94905c89f8e16b79eed8f64 Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
@@ -32,6 +32,7 @@ import StudioTheme as StudioTheme
|
||||
import StudioControls as SC
|
||||
|
||||
import NewProjectDialog
|
||||
import BackendApi
|
||||
|
||||
Item {
|
||||
id: rootDialog
|
||||
@@ -161,17 +162,43 @@ Item {
|
||||
readonly property int animDur: 500
|
||||
id: tabBar
|
||||
x: 10 // left padding
|
||||
width: parent.width - 64 // right padding
|
||||
width: parent.width - 20 // right padding
|
||||
height: DialogValues.projectViewHeaderHeight
|
||||
color: DialogValues.lightPaneColor
|
||||
|
||||
function selectTab(tabIndex, selectLast = false) {
|
||||
var item = repeater.itemAt(tabIndex)
|
||||
tabBarRow.currIndex = tabIndex
|
||||
|
||||
projectView.selectLast = selectLast
|
||||
BackendApi.presetModel.setPage(tabIndex) // NOTE: it resets preset model
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: BackendApi
|
||||
|
||||
function onUserPresetSaved() {
|
||||
var customTabIndex = repeater.count - 1
|
||||
tabBar.selectTab(customTabIndex, true)
|
||||
}
|
||||
|
||||
function onLastUserPresetRemoved() {
|
||||
tabBar.selectTab(0, false)
|
||||
}
|
||||
}
|
||||
|
||||
Row {
|
||||
id: tabBarRow
|
||||
spacing: 20
|
||||
property int currIndex: 0
|
||||
readonly property string currentTabName:
|
||||
repeater.count > 0 && repeater.itemAt(currIndex)
|
||||
? repeater.itemAt(currIndex).text
|
||||
: ''
|
||||
|
||||
Repeater {
|
||||
model: categoryModel
|
||||
id: repeater
|
||||
model: BackendApi.categoryModel
|
||||
Text {
|
||||
text: name
|
||||
font.weight: Font.DemiBold
|
||||
@@ -184,13 +211,7 @@ Item {
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
tabBarRow.currIndex = index
|
||||
presetModel.setPage(index)
|
||||
projectView.currentIndex = 0
|
||||
projectView.currentIndexChanged()
|
||||
|
||||
strip.x = parent.x
|
||||
strip.width = parent.width
|
||||
tabBar.selectTab(index)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,8 +220,19 @@ Item {
|
||||
} // tabBarRow
|
||||
|
||||
Rectangle {
|
||||
function computeX() {
|
||||
var item = tabBarRow.children[tabBarRow.currIndex] ?? tabBarRow.children[0]
|
||||
return item.x;
|
||||
}
|
||||
|
||||
function computeWidth() {
|
||||
var item = tabBarRow.children[tabBarRow.currIndex] ?? tabBarRow.children[0]
|
||||
return item.width;
|
||||
}
|
||||
|
||||
id: strip
|
||||
width: tabBarRow.children[0].width
|
||||
x: computeX()
|
||||
width: computeWidth()
|
||||
height: 5
|
||||
radius: 2
|
||||
color: DialogValues.textColorInteraction
|
||||
@@ -209,35 +241,40 @@ Item {
|
||||
Behavior on x { SmoothedAnimation { duration: tabBar.animDur } }
|
||||
Behavior on width { SmoothedAnimation { duration: strip.width === 0 ? 0 : tabBar.animDur } } // do not animate initial width
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: rootDialog
|
||||
function onWidthChanged() {
|
||||
if (rootDialog.width < 1200) { // 1200 = the width threshold
|
||||
tabBar.width = tabBar.parent.width - 20
|
||||
projectView.width = projectView.parent.width - 20
|
||||
} else {
|
||||
tabBar.width = tabBar.parent.width - 64
|
||||
projectView.width = projectView.parent.width - 64
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Rectangle
|
||||
|
||||
NewProjectView {
|
||||
id: projectView
|
||||
Rectangle {
|
||||
id: projectViewFrame
|
||||
x: 10 // left padding
|
||||
width: parent.width - 64 // right padding
|
||||
width: parent.width - 20 // right padding
|
||||
height: DialogValues.projectViewHeight
|
||||
loader: projectDetailsLoader
|
||||
color: DialogValues.darkPaneColor
|
||||
|
||||
Connections {
|
||||
target: rootDialog
|
||||
function onHeightChanged() {
|
||||
if (rootDialog.height < 700) { // 700 = minimum height big dialog
|
||||
projectView.height = DialogValues.projectViewHeight / 2
|
||||
} else {
|
||||
projectView.height = DialogValues.projectViewHeight
|
||||
Item {
|
||||
anchors.fill: parent
|
||||
anchors.margins: DialogValues.gridMargins
|
||||
|
||||
NewProjectView {
|
||||
id: projectView
|
||||
anchors.fill: parent
|
||||
|
||||
loader: projectDetailsLoader
|
||||
currentTabName: tabBarRow.currentTabName
|
||||
|
||||
Connections {
|
||||
target: rootDialog
|
||||
function onHeightChanged() {
|
||||
if (rootDialog.height < 720) { // 720 = minimum height big dialog
|
||||
DialogValues.projectViewHeight =
|
||||
DialogValues.projectItemHeight
|
||||
+ 2 * DialogValues.gridMargins
|
||||
} else {
|
||||
DialogValues.projectViewHeight =
|
||||
DialogValues.projectItemHeight * 2
|
||||
+ DialogValues.gridSpacing
|
||||
+ 2 * DialogValues.gridMargins
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -247,12 +284,12 @@ Item {
|
||||
|
||||
Text {
|
||||
id: descriptionText
|
||||
text: dialogBox.projectDescription
|
||||
text: BackendApi.projectDescription
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
lineHeight: DialogValues.defaultLineHeight
|
||||
lineHeightMode: Text.FixedHeight
|
||||
leftPadding: 14
|
||||
width: projectView.width
|
||||
width: projectViewFrame.width
|
||||
color: DialogValues.textColor
|
||||
wrapMode: Text.WordWrap
|
||||
maximumLineCount: 4
|
||||
@@ -298,7 +335,7 @@ Item {
|
||||
iconFont: StudioTheme.Constants.font
|
||||
|
||||
onClicked: {
|
||||
dialogBox.reject();
|
||||
BackendApi.reject();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -310,11 +347,11 @@ Item {
|
||||
visible: true
|
||||
buttonIcon: qsTr("Create")
|
||||
iconSize: DialogValues.defaultPixelSize
|
||||
enabled: dialogBox.fieldsValid
|
||||
enabled: BackendApi.fieldsValid
|
||||
iconFont: StudioTheme.Constants.font
|
||||
|
||||
onClicked: {
|
||||
dialogBox.accept();
|
||||
BackendApi.accept();
|
||||
}
|
||||
}
|
||||
} // RowLayout
|
||||
|
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 5.5 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.2 KiB |
Before Width: | Height: | Size: 4.1 KiB After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 8.7 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 5.0 KiB |
Before Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 2.2 KiB |
After Width: | Height: | Size: 4.1 KiB |
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 3.7 KiB After Width: | Height: | Size: 3.5 KiB |
Before Width: | Height: | Size: 9.3 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 3.8 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 9.4 KiB After Width: | Height: | Size: 5.3 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 3.4 KiB |
Before Width: | Height: | Size: 8.9 KiB After Width: | Height: | Size: 5.2 KiB |
Before Width: | Height: | Size: 3.3 KiB After Width: | Height: | Size: 3.3 KiB |
Before Width: | Height: | Size: 8.3 KiB After Width: | Height: | Size: 5.1 KiB |
@@ -31,16 +31,13 @@ import QtQuick.Layouts
|
||||
import StudioControls as SC
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
import BackendApi
|
||||
|
||||
Item {
|
||||
width: DialogValues.detailsPaneWidth
|
||||
|
||||
Component.onCompleted: {
|
||||
dialogBox.detailsLoaded = true;
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
dialogBox.detailsLoaded = false;
|
||||
}
|
||||
Component.onCompleted: BackendApi.detailsLoaded = true
|
||||
Component.onDestruction: BackendApi.detailsLoaded = false
|
||||
|
||||
Rectangle {
|
||||
color: DialogValues.darkPaneColor
|
||||
@@ -53,13 +50,13 @@ Item {
|
||||
|
||||
Column {
|
||||
anchors.fill: parent
|
||||
spacing: DialogValues.defaultPadding
|
||||
spacing: 5
|
||||
|
||||
Text {
|
||||
id: detailsHeading
|
||||
text: qsTr("Details")
|
||||
height: DialogValues.paneTitleTextHeight
|
||||
width: parent.width;
|
||||
width: parent.width
|
||||
font.weight: Font.DemiBold
|
||||
font.pixelSize: DialogValues.paneTitlePixelSize
|
||||
lineHeight: DialogValues.paneTitleLineHeight
|
||||
@@ -71,39 +68,36 @@ Item {
|
||||
Flickable {
|
||||
width: parent.width
|
||||
height: parent.height - detailsHeading.height - DialogValues.defaultPadding
|
||||
|
||||
- savePresetButton.height
|
||||
contentWidth: parent.width
|
||||
contentHeight: scrollContent.height
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
clip: true
|
||||
|
||||
ScrollBar.vertical: SC.VerticalScrollBar {
|
||||
}
|
||||
ScrollBar.vertical: SC.VerticalScrollBar {}
|
||||
|
||||
Column {
|
||||
id: scrollContent
|
||||
width: parent.width - DialogValues.detailsPanePadding
|
||||
height: DialogValues.detailsScrollableContentHeight
|
||||
spacing: DialogValues.defaultPadding
|
||||
|
||||
SC.TextField {
|
||||
id: projectNameTextField
|
||||
actionIndicatorVisible: false
|
||||
translationIndicatorVisible: false
|
||||
text: dialogBox.projectName
|
||||
text: BackendApi.projectName
|
||||
width: parent.width
|
||||
color: DialogValues.textColor
|
||||
selectByMouse: true
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
|
||||
onEditingFinished: {
|
||||
text = text.charAt(0).toUpperCase() + text.slice(1)
|
||||
}
|
||||
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: dialogBox
|
||||
target: BackendApi
|
||||
property: "projectName"
|
||||
value: projectNameTextField.text
|
||||
}
|
||||
@@ -118,14 +112,14 @@ Item {
|
||||
id: projectLocationTextField
|
||||
actionIndicatorVisible: false
|
||||
translationIndicatorVisible: false
|
||||
text: dialogBox.projectLocation
|
||||
text: BackendApi.projectLocation
|
||||
color: DialogValues.textColor
|
||||
selectByMouse: true
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: dialogBox
|
||||
target: BackendApi
|
||||
property: "projectLocation"
|
||||
value: projectLocationTextField.text
|
||||
}
|
||||
@@ -138,7 +132,7 @@ Item {
|
||||
iconFont: StudioTheme.Constants.font
|
||||
|
||||
onClicked: {
|
||||
var newLocation = dialogBox.chooseProjectLocation()
|
||||
var newLocation = BackendApi.chooseProjectLocation()
|
||||
if (newLocation)
|
||||
projectLocationTextField.text = newLocation
|
||||
}
|
||||
@@ -159,7 +153,7 @@ Item {
|
||||
|
||||
Text {
|
||||
id: statusMessage
|
||||
text: dialogBox.statusMessage
|
||||
text: BackendApi.statusMessage
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
lineHeight: DialogValues.defaultLineHeight
|
||||
lineHeightMode: Text.FixedHeight
|
||||
@@ -172,7 +166,7 @@ Item {
|
||||
states: [
|
||||
State {
|
||||
name: "warning"
|
||||
when: dialogBox.statusType === "warning"
|
||||
when: BackendApi.statusType === "warning"
|
||||
PropertyChanges {
|
||||
target: statusMessage
|
||||
color: DialogValues.textWarning
|
||||
@@ -185,7 +179,7 @@ Item {
|
||||
|
||||
State {
|
||||
name: "error"
|
||||
when: dialogBox.statusType === "error"
|
||||
when: BackendApi.statusType === "error"
|
||||
PropertyChanges {
|
||||
target: statusMessage
|
||||
color: DialogValues.textError
|
||||
@@ -208,7 +202,7 @@ Item {
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: dialogBox
|
||||
target: BackendApi
|
||||
property: "saveAsDefaultLocation"
|
||||
value: defaultLocationCheckbox.checked
|
||||
}
|
||||
@@ -219,25 +213,25 @@ Item {
|
||||
id: screenSizeComboBox
|
||||
actionIndicatorVisible: false
|
||||
currentIndex: -1
|
||||
model: screenSizeModel
|
||||
model: BackendApi.screenSizeModel
|
||||
textRole: "display"
|
||||
width: parent.width
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
|
||||
onActivated: (index) => {
|
||||
dialogBox.setScreenSizeIndex(index);
|
||||
BackendApi.setScreenSizeIndex(index);
|
||||
|
||||
var size = screenSizeModel.screenSizes(index);
|
||||
var size = BackendApi.screenSizeModel.screenSizes(index);
|
||||
widthField.realValue = size.width;
|
||||
heightField.realValue = size.height;
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: screenSizeModel
|
||||
target: BackendApi.screenSizeModel
|
||||
function onModelReset() {
|
||||
var newIndex = screenSizeComboBox.currentIndex > -1
|
||||
? screenSizeComboBox.currentIndex
|
||||
: dialogBox.screenSizeIndex()
|
||||
: BackendApi.screenSizeIndex()
|
||||
|
||||
screenSizeComboBox.currentIndex = newIndex
|
||||
screenSizeComboBox.activated(newIndex)
|
||||
@@ -248,10 +242,8 @@ Item {
|
||||
GridLayout { // orientation + width + height
|
||||
width: parent.width
|
||||
height: 85
|
||||
|
||||
columns: 4
|
||||
rows: 2
|
||||
|
||||
columnSpacing: 10
|
||||
rowSpacing: 10
|
||||
|
||||
@@ -295,10 +287,7 @@ Item {
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
|
||||
onRealValueChanged: {
|
||||
var height = heightField.realValue
|
||||
var width = realValue
|
||||
|
||||
if (width >= height)
|
||||
if (widthField.realValue >= heightField.realValue)
|
||||
orientationButton.setHorizontal()
|
||||
else
|
||||
orientationButton.setVertical()
|
||||
@@ -306,7 +295,7 @@ Item {
|
||||
} // Width Text Field
|
||||
|
||||
Binding {
|
||||
target: dialogBox
|
||||
target: BackendApi
|
||||
property: "customWidth"
|
||||
value: widthField.realValue
|
||||
}
|
||||
@@ -323,10 +312,7 @@ Item {
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
|
||||
onRealValueChanged: {
|
||||
var height = realValue
|
||||
var width = widthField.realValue
|
||||
|
||||
if (width >= height)
|
||||
if (widthField.realValue >= heightField.realValue)
|
||||
orientationButton.setHorizontal()
|
||||
else
|
||||
orientationButton.setVertical()
|
||||
@@ -334,7 +320,7 @@ Item {
|
||||
} // Height Text Field
|
||||
|
||||
Binding {
|
||||
target: dialogBox
|
||||
target: BackendApi
|
||||
property: "customHeight"
|
||||
value: heightField.realValue
|
||||
}
|
||||
@@ -345,7 +331,6 @@ Item {
|
||||
id: orientationButton
|
||||
implicitWidth: 100
|
||||
implicitHeight: 50
|
||||
|
||||
checked: false
|
||||
hoverEnabled: false
|
||||
background: Rectangle {
|
||||
@@ -384,19 +369,19 @@ Item {
|
||||
|
||||
onClicked: {
|
||||
if (widthField.realValue && heightField.realValue) {
|
||||
[widthField.realValue, heightField.realValue] = [heightField.realValue, widthField.realValue];
|
||||
checked = !checked
|
||||
[widthField.realValue, heightField.realValue] = [heightField.realValue, widthField.realValue]
|
||||
orientationButton.checked = !orientationButton.checked
|
||||
}
|
||||
}
|
||||
|
||||
function setHorizontal() {
|
||||
checked = false
|
||||
orientationButton.checked = false
|
||||
horizontalBar.color = DialogValues.textColorInteraction
|
||||
verticalBar.color = "white"
|
||||
}
|
||||
|
||||
function setVertical() {
|
||||
checked = true
|
||||
orientationButton.checked = true
|
||||
horizontalBar.color = "white"
|
||||
verticalBar.color = DialogValues.textColorInteraction
|
||||
}
|
||||
@@ -404,23 +389,27 @@ Item {
|
||||
|
||||
} // GridLayout: orientation + width + height
|
||||
|
||||
Rectangle { width: parent.width; height: 1; color: DialogValues.dividerlineColor }
|
||||
Rectangle {
|
||||
width: parent.width
|
||||
height: 1
|
||||
color: DialogValues.dividerlineColor
|
||||
}
|
||||
|
||||
SC.CheckBox {
|
||||
id: useQtVirtualKeyboard
|
||||
actionIndicatorVisible: false
|
||||
text: qsTr("Use Qt Virtual Keyboard")
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
checked: dialogBox.useVirtualKeyboard
|
||||
visible: dialogBox.haveVirtualKeyboard
|
||||
checked: BackendApi.useVirtualKeyboard
|
||||
visible: BackendApi.haveVirtualKeyboard
|
||||
}
|
||||
|
||||
RowLayout { // Target Qt Version
|
||||
width: parent.width
|
||||
visible: dialogBox.haveTargetQtVersion
|
||||
visible: BackendApi.haveTargetQtVersion
|
||||
|
||||
Text {
|
||||
text: "Target Qt Version:"
|
||||
text: qsTr("Target Qt Version:")
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
lineHeight: DialogValues.defaultLineHeight
|
||||
lineHeightMode: Text.FixedHeight
|
||||
@@ -432,33 +421,98 @@ Item {
|
||||
actionIndicatorVisible: false
|
||||
implicitWidth: 70
|
||||
Layout.alignment: Qt.AlignRight
|
||||
currentIndex: 1
|
||||
currentIndex: BackendApi.targetQtVersionIndex
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
|
||||
model: ListModel {
|
||||
ListElement {
|
||||
name: "Qt 5"
|
||||
}
|
||||
ListElement {
|
||||
name: "Qt 6"
|
||||
}
|
||||
ListElement { name: "Qt 5" }
|
||||
ListElement { name: "Qt 6" }
|
||||
}
|
||||
|
||||
onActivated: (index) => {
|
||||
dialogBox.setTargetQtVersion(index)
|
||||
BackendApi.targetQtVersionIndex = index
|
||||
}
|
||||
} // Target Qt Version ComboBox
|
||||
|
||||
Binding {
|
||||
target: BackendApi
|
||||
property: "targetQtVersionIndex"
|
||||
value: qtVersionComboBox.currentIndex
|
||||
}
|
||||
|
||||
} // RowLayout
|
||||
|
||||
Binding {
|
||||
target: dialogBox
|
||||
target: BackendApi
|
||||
property: "useVirtualKeyboard"
|
||||
value: useQtVirtualKeyboard.checked
|
||||
}
|
||||
} // ScrollContent Column
|
||||
} // ScrollView
|
||||
|
||||
} // Column
|
||||
|
||||
SC.AbstractButton {
|
||||
id: savePresetButton
|
||||
width: StudioTheme.Values.singleControlColumnWidth
|
||||
buttonIcon: qsTr("Save Custom Preset")
|
||||
iconFont: StudioTheme.Constants.font
|
||||
iconSize: DialogValues.defaultPixelSize
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
onClicked: savePresetDialog.open()
|
||||
}
|
||||
|
||||
PopupDialog {
|
||||
id: savePresetDialog
|
||||
title: qsTr("Save Preset")
|
||||
standardButtons: Dialog.Save | Dialog.Cancel
|
||||
modal: true
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
anchors.centerIn: parent
|
||||
width: DialogValues.popupDialogWidth
|
||||
|
||||
onAccepted: BackendApi.savePresetDialogAccept()
|
||||
|
||||
onOpened: {
|
||||
presetNameTextField.selectAll()
|
||||
presetNameTextField.forceActiveFocus()
|
||||
}
|
||||
|
||||
ColumnLayout {
|
||||
width: parent.width
|
||||
spacing: 10
|
||||
|
||||
Text {
|
||||
text: qsTr("Preset name")
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
color: DialogValues.textColor
|
||||
}
|
||||
|
||||
SC.TextField {
|
||||
id: presetNameTextField
|
||||
actionIndicatorVisible: false
|
||||
translationIndicatorVisible: false
|
||||
text: qsTr("MyPreset")
|
||||
color: DialogValues.textColor
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
Layout.fillWidth: true
|
||||
maximumLength: 30
|
||||
validator: RegularExpressionValidator { regularExpression: /\w[\w ]*/ }
|
||||
|
||||
onEditingFinished: {
|
||||
presetNameTextField.text = text.trim()
|
||||
presetNameTextField.text = text.replace(/\s+/g, " ")
|
||||
}
|
||||
}
|
||||
|
||||
Binding {
|
||||
target: BackendApi
|
||||
property: "presetName"
|
||||
value: presetNameTextField.text
|
||||
}
|
||||
}
|
||||
}
|
||||
} // Item
|
||||
}
|
||||
}
|
||||
} // Rectangle
|
||||
} // root Item
|
||||
|
@@ -29,39 +29,57 @@ import QtQml
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
QtObject {
|
||||
id: root
|
||||
|
||||
readonly property int dialogWidth: 1522
|
||||
readonly property int dialogHeight: 940
|
||||
readonly property int projectViewMinimumWidth: 600
|
||||
readonly property int projectViewMinimumHeight: projectViewHeight
|
||||
readonly property int dialogContentHeight: projectViewHeight + 300 // i.e. dialog without header and footer
|
||||
readonly property int loadedPanesWidth: detailsPaneWidth + stylesPaneWidth
|
||||
readonly property int detailsPaneWidth: 330 + detailsPanePadding * 2
|
||||
readonly property int projectViewMinimumHeight: root.gridCellHeight
|
||||
readonly property int dialogContentHeight: root.projectViewHeight + 300 // i.e. dialog without header and footer
|
||||
readonly property int loadedPanesWidth: root.detailsPaneWidth + root.stylesPaneWidth
|
||||
readonly property int detailsPaneWidth: 330 + root.detailsPanePadding * 2
|
||||
readonly property int dialogTitleTextHeight: 85
|
||||
readonly property int paneTitleTextHeight: 47
|
||||
readonly property int logoWidth: 85
|
||||
readonly property int logoHeight: 85
|
||||
|
||||
/* detailsScrollableContentHeight - the full height that may need to be scrolled to be fully
|
||||
visible, if the dialog box is too small. */
|
||||
readonly property int detailsScrollableContentHeight: 428
|
||||
readonly property int stylesPaneWidth: styleImageWidth + stylesPanePadding * 2 + styleImageBorderWidth * 2 // i.e. 240px
|
||||
readonly property int stylesPaneWidth: root.styleImageWidth + root.stylesPanePadding * 2
|
||||
+ root.styleImageBorderWidth * 2 // i.e. 240px
|
||||
readonly property int detailsPanePadding: 18
|
||||
readonly property int stylesPanePadding: 18
|
||||
readonly property int defaultPadding: 18
|
||||
readonly property int dialogLeftPadding: 35
|
||||
|
||||
readonly property int styleListItemHeight: root.styleImageHeight + root.styleTextHeight
|
||||
+ 2 * root.styleImageBorderWidth
|
||||
+ root.styleListItemBottomMargin
|
||||
+ root.styleListItemSpacing
|
||||
readonly property int styleListItemBottomMargin: 10
|
||||
readonly property int styleListItemSpacing: 4
|
||||
readonly property int styleImageWidth: 200
|
||||
readonly property int styleImageHeight: 262
|
||||
readonly property int styleImageBorderWidth: 2
|
||||
readonly property int styleTextHeight: 18
|
||||
|
||||
readonly property int footerHeight: 73
|
||||
readonly property int projectItemWidth: 90
|
||||
readonly property int projectItemHeight: 144
|
||||
readonly property int projectViewHeight: projectItemHeight * 2
|
||||
readonly property int projectItemWidth: 136
|
||||
readonly property int projectItemHeight: 110
|
||||
property int projectViewHeight: root.projectItemHeight * 2 + root.gridSpacing + root.gridMargins * 2
|
||||
readonly property int projectViewHeaderHeight: 38
|
||||
|
||||
readonly property int gridMargins: 20
|
||||
readonly property int gridCellWidth: root.projectItemWidth + root.gridSpacing
|
||||
readonly property int gridCellHeight: root.projectItemHeight + root.gridSpacing
|
||||
readonly property int gridSpacing: 2
|
||||
|
||||
readonly property int dialogButtonWidth: 100
|
||||
|
||||
readonly property int loadedPanesHeight: dialogContentHeight
|
||||
readonly property int detailsPaneHeight: dialogContentHeight
|
||||
// This is for internal popup dialogs
|
||||
readonly property int popupDialogWidth: 270
|
||||
readonly property int popupDialogPadding: 12
|
||||
|
||||
readonly property int loadedPanesHeight: root.dialogContentHeight
|
||||
readonly property int detailsPaneHeight: root.dialogContentHeight
|
||||
|
||||
readonly property string darkPaneColor: StudioTheme.Values.themeBackgroundColorNormal
|
||||
readonly property string lightPaneColor: StudioTheme.Values.themeBackgroundColorAlternate
|
||||
@@ -71,6 +89,8 @@ QtObject {
|
||||
readonly property string dividerlineColor: StudioTheme.Values.themeTextColorDisabled
|
||||
readonly property string textError: StudioTheme.Values.themeError
|
||||
readonly property string textWarning: StudioTheme.Values.themeWarning
|
||||
readonly property string presetItemBackgroundHover: StudioTheme.Values.themeControlBackgroundGlobalHover
|
||||
readonly property string presetItemBackgroundHoverInteraction: StudioTheme.Values.themeControlBackgroundInteraction
|
||||
|
||||
readonly property real defaultPixelSize: 14
|
||||
readonly property real defaultLineHeight: 21
|
||||
@@ -91,6 +111,6 @@ QtObject {
|
||||
item and spacing after it). So we have to subtract 2 x layout spacing before setting
|
||||
our own, narrower, spacing.
|
||||
*/
|
||||
return -layoutSpacing -layoutSpacing + value
|
||||
return -layoutSpacing - layoutSpacing + value
|
||||
}
|
||||
}
|
||||
|
@@ -28,111 +28,252 @@ import QtQuick.Controls
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
import StudioControls as SC
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
GridView {
|
||||
id: projectView
|
||||
import BackendApi
|
||||
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
|
||||
required property Item loader
|
||||
required property string currentTabName
|
||||
|
||||
cellWidth: DialogValues.projectItemWidth
|
||||
cellHeight: DialogValues.projectItemHeight
|
||||
clip: true
|
||||
property string backgroundHoverColor: DialogValues.presetItemBackgroundHover
|
||||
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
// selectLast: if true, it will select last item in the model after a model reset.
|
||||
property bool selectLast: false
|
||||
|
||||
children: [
|
||||
Rectangle {
|
||||
color: DialogValues.darkPaneColor
|
||||
anchors.fill: parent
|
||||
z: -1
|
||||
}
|
||||
]
|
||||
|
||||
model: presetModel
|
||||
|
||||
// called by onModelReset and when user clicks on an item, or when the header item is changed.
|
||||
onCurrentIndexChanged: {
|
||||
dialogBox.selectedPreset = projectView.currentIndex
|
||||
var source = dialogBox.currentPresetQmlPath()
|
||||
loader.source = source
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical: SC.VerticalScrollBar {
|
||||
parent: scrollView
|
||||
x: scrollView.width + (DialogValues.gridMargins
|
||||
- StudioTheme.Values.scrollBarThickness) * 0.5
|
||||
y: scrollView.topPadding
|
||||
height: scrollView.availableHeight
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: presetModel
|
||||
contentWidth: gridView.contentItem.childrenRect.width
|
||||
contentHeight: gridView.contentItem.childrenRect.height
|
||||
|
||||
// called when data is set (setWizardFactories)
|
||||
function onModelReset() {
|
||||
currentIndex = 0
|
||||
currentIndexChanged()
|
||||
}
|
||||
}
|
||||
GridView {
|
||||
id: gridView
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: delegate
|
||||
clip: true
|
||||
anchors.fill: parent
|
||||
cellWidth: DialogValues.gridCellWidth
|
||||
cellHeight: DialogValues.gridCellHeight
|
||||
rightMargin: -DialogValues.gridSpacing
|
||||
bottomMargin: -DialogValues.gridSpacing
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
model: BackendApi.presetModel
|
||||
|
||||
width: DialogValues.projectItemWidth
|
||||
height: DialogValues.projectItemHeight
|
||||
background: null
|
||||
|
||||
function fontIconCode(index) {
|
||||
var code = presetModel.fontIconCode(index)
|
||||
return code ? code : StudioTheme.Constants.wizardsUnknown
|
||||
// called by onModelReset and when user clicks on an item, or when the header item is changed.
|
||||
onCurrentIndexChanged: {
|
||||
BackendApi.selectedPreset = gridView.currentIndex
|
||||
var source = BackendApi.currentPresetQmlPath()
|
||||
scrollView.loader.source = source
|
||||
}
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
Connections {
|
||||
target: BackendApi.presetModel
|
||||
|
||||
Label {
|
||||
id: projectTypeIcon
|
||||
text: fontIconCode(index)
|
||||
color: DialogValues.textColor
|
||||
width: parent.width
|
||||
height: DialogValues.projectItemHeight / 2
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
renderType: Text.NativeRendering
|
||||
font.pixelSize: 65
|
||||
font.family: StudioTheme.Constants.iconFont.family
|
||||
// called when data is set (setWizardFactories)
|
||||
function onModelReset() {
|
||||
if (scrollView.selectLast) {
|
||||
gridView.currentIndex = BackendApi.presetModel.rowCount() - 1
|
||||
scrollView.selectLast = false
|
||||
} else {
|
||||
gridView.currentIndex = 0
|
||||
}
|
||||
|
||||
// This will load Details.qml and Styles.qml by setting "source" on the Loader.
|
||||
gridView.currentIndexChanged()
|
||||
}
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: delegate
|
||||
|
||||
property bool hover: delegate.hovered || removeMouseArea.containsMouse
|
||||
|
||||
width: DialogValues.projectItemWidth
|
||||
height: DialogValues.projectItemHeight
|
||||
|
||||
onClicked: delegate.GridView.view.currentIndex = index
|
||||
|
||||
background: Rectangle {
|
||||
id: delegateBackground
|
||||
width: parent.width
|
||||
height: parent.height
|
||||
color: delegate.hover ? scrollView.backgroundHoverColor : "transparent"
|
||||
border.color: delegate.hover ? projectTypeName.color : "transparent"
|
||||
}
|
||||
|
||||
function fontIconCode(index) {
|
||||
var code = BackendApi.presetModel.fontIconCode(index)
|
||||
return code ? code : StudioTheme.Constants.wizardsUnknown
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: parent
|
||||
|
||||
ColumnLayout {
|
||||
spacing: 0
|
||||
anchors.top: parent.top
|
||||
anchors.topMargin: -1
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
|
||||
Label {
|
||||
id: projectTypeIcon
|
||||
text: delegate.fontIconCode(index)
|
||||
color: DialogValues.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignBottom
|
||||
renderType: Text.NativeRendering
|
||||
font.pixelSize: 65
|
||||
font.family: StudioTheme.Constants.iconFont.family
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
} // Preset type icon Label
|
||||
|
||||
Text {
|
||||
id: projectTypeName
|
||||
color: DialogValues.textColor
|
||||
text: name
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
lineHeight: DialogValues.defaultLineHeight
|
||||
lineHeightMode: Text.FixedHeight
|
||||
width: DialogValues.projectItemWidth - 16
|
||||
elide: Text.ElideRight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignTop
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
Layout.preferredWidth: projectTypeName.width
|
||||
Layout.minimumWidth: projectTypeName.width
|
||||
Layout.maximumWidth: projectTypeName.width
|
||||
|
||||
ToolTip {
|
||||
id: toolTip
|
||||
y: -toolTip.height
|
||||
visible: delegate.hovered && projectTypeName.truncated
|
||||
text: name
|
||||
delay: 1000
|
||||
height: 20
|
||||
|
||||
background: Rectangle {
|
||||
color: StudioTheme.Values.themeToolTipBackground
|
||||
border.color: StudioTheme.Values.themeToolTipOutline
|
||||
border.width: StudioTheme.Values.border
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
color: StudioTheme.Values.themeToolTipText
|
||||
text: toolTip.text
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Text {
|
||||
id: projectTypeResolution
|
||||
color: DialogValues.textColor
|
||||
text: resolution
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
lineHeight: DialogValues.defaultLineHeight
|
||||
lineHeightMode: Text.FixedHeight
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignTop
|
||||
Layout.alignment: Qt.AlignHCenter
|
||||
}
|
||||
|
||||
} // ColumnLayout
|
||||
|
||||
Item {
|
||||
id: removePresetButton
|
||||
width: 20
|
||||
height: 20
|
||||
anchors.top: parent.top
|
||||
anchors.right: parent.right
|
||||
anchors.margins: 4
|
||||
visible: isUserPreset === true
|
||||
&& delegate.hover
|
||||
&& scrollView.currentTabName !== "Recents"
|
||||
|
||||
Text {
|
||||
anchors.fill: parent
|
||||
text: StudioTheme.Constants.closeCross
|
||||
color: DialogValues.textColor
|
||||
horizontalAlignment: Qt.AlignHCenter
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
font.family: StudioTheme.Constants.iconFont.family
|
||||
font.pixelSize: StudioTheme.Values.myIconFontSize
|
||||
}
|
||||
|
||||
MouseArea {
|
||||
id: removeMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
cursorShape: removeMouseArea.containsMouse ? Qt.PointingHandCursor
|
||||
: Qt.ArrowCursor
|
||||
|
||||
onClicked: {
|
||||
removePresetDialog.presetName = projectTypeName.text
|
||||
removePresetDialog.open()
|
||||
}
|
||||
}
|
||||
} // Delete preset button Item
|
||||
} // Item
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "current"
|
||||
when: delegate.GridView.isCurrentItem
|
||||
|
||||
PropertyChanges {
|
||||
target: projectTypeName
|
||||
color: DialogValues.textColorInteraction
|
||||
}
|
||||
PropertyChanges {
|
||||
target: projectTypeResolution
|
||||
color: DialogValues.textColorInteraction
|
||||
}
|
||||
PropertyChanges {
|
||||
target: projectTypeIcon
|
||||
color: DialogValues.textColorInteraction
|
||||
}
|
||||
PropertyChanges {
|
||||
target: scrollView
|
||||
backgroundHoverColor: DialogValues.presetItemBackgroundHoverInteraction
|
||||
}
|
||||
} // State
|
||||
]
|
||||
} // ItemDelegate
|
||||
|
||||
PopupDialog {
|
||||
id: removePresetDialog
|
||||
|
||||
property string presetName
|
||||
|
||||
title: qsTr("Delete Custom Preset")
|
||||
standardButtons: Dialog.Yes | Dialog.No
|
||||
modal: true
|
||||
closePolicy: Popup.CloseOnEscape
|
||||
anchors.centerIn: parent
|
||||
width: DialogValues.popupDialogWidth
|
||||
|
||||
onAccepted: BackendApi.removeCurrentPreset()
|
||||
|
||||
Text {
|
||||
id: projectTypeLabel
|
||||
text: qsTr("Are you sure you want to delete \"" + removePresetDialog.presetName + "\" ?")
|
||||
color: DialogValues.textColor
|
||||
|
||||
text: name
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
lineHeight: DialogValues.defaultLineHeight
|
||||
lineHeightMode: Text.FixedHeight
|
||||
width: parent.width
|
||||
height: DialogValues.projectItemHeight / 2
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignTop
|
||||
wrapMode: Text.WordWrap
|
||||
|
||||
width: DialogValues.popupDialogWidth - 2 * DialogValues.popupDialogPadding
|
||||
}
|
||||
} // Column
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
delegate.GridView.view.currentIndex = index
|
||||
}
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
when: delegate.GridView.isCurrentItem
|
||||
PropertyChanges {
|
||||
target: projectTypeLabel
|
||||
color: DialogValues.textColorInteraction
|
||||
}
|
||||
|
||||
PropertyChanges {
|
||||
target: projectTypeIcon
|
||||
color: DialogValues.textColorInteraction
|
||||
}
|
||||
} // State
|
||||
]
|
||||
} // ItemDelegate
|
||||
} // GridView
|
||||
} // Dialog
|
||||
} // GridView
|
||||
} // ScrollView
|
||||
|
@@ -0,0 +1,66 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import QtQuick.Layouts
|
||||
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
import BackendApi
|
||||
|
||||
Dialog {
|
||||
id: root
|
||||
padding: DialogValues.popupDialogPadding
|
||||
|
||||
background: Rectangle {
|
||||
color: DialogValues.darkPaneColor
|
||||
border.color: StudioTheme.Values.themeInteraction
|
||||
border.width: StudioTheme.Values.border
|
||||
}
|
||||
|
||||
header: Label {
|
||||
text: root.title
|
||||
visible: root.title
|
||||
elide: Label.ElideRight
|
||||
font.bold: true
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
padding: DialogValues.popupDialogPadding
|
||||
color: DialogValues.textColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
|
||||
background: Rectangle {
|
||||
x: 1
|
||||
y: 1
|
||||
width: parent.width - 2
|
||||
height: parent.height - 1
|
||||
color: DialogValues.darkPaneColor
|
||||
}
|
||||
}
|
||||
|
||||
footer: PopupDialogButtonBox {
|
||||
visible: count > 0
|
||||
}
|
||||
}
|
@@ -0,0 +1,107 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
Button {
|
||||
id: root
|
||||
|
||||
implicitWidth: Math.max(
|
||||
background ? background.implicitWidth : 0,
|
||||
textItem.implicitWidth + leftPadding + rightPadding)
|
||||
implicitHeight: Math.max(
|
||||
background ? background.implicitHeight : 0,
|
||||
textItem.implicitHeight + topPadding + bottomPadding)
|
||||
leftPadding: 4
|
||||
rightPadding: 4
|
||||
|
||||
background: Rectangle {
|
||||
id: background
|
||||
implicitWidth: 80
|
||||
implicitHeight: StudioTheme.Values.height
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
contentItem: Text {
|
||||
id: textItem
|
||||
text: root.text
|
||||
font.family: StudioTheme.Constants.font.family
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
verticalAlignment: Text.AlignVCenter
|
||||
renderType: Text.QtRendering
|
||||
anchors.fill: parent
|
||||
}
|
||||
|
||||
states: [
|
||||
State {
|
||||
name: "default"
|
||||
when: !root.hovered && !root.checked && !root.pressed
|
||||
|
||||
PropertyChanges {
|
||||
target: background
|
||||
color: StudioTheme.Values.themeControlBackground
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
}
|
||||
PropertyChanges {
|
||||
target: textItem
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "hover"
|
||||
when: root.hovered && !root.checked && !root.pressed
|
||||
|
||||
PropertyChanges {
|
||||
target: background
|
||||
color: StudioTheme.Values.themeControlBackgroundHover
|
||||
border.color: StudioTheme.Values.themeControlOutline
|
||||
}
|
||||
PropertyChanges {
|
||||
target: textItem
|
||||
color: StudioTheme.Values.themeTextColor
|
||||
}
|
||||
},
|
||||
State {
|
||||
name: "press"
|
||||
when: root.hovered && root.pressed
|
||||
|
||||
PropertyChanges {
|
||||
target: background
|
||||
color: StudioTheme.Values.themeInteraction
|
||||
border.color: StudioTheme.Values.themeInteraction
|
||||
}
|
||||
PropertyChanges {
|
||||
target: textItem
|
||||
color: StudioTheme.Values.themeIconColor
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
@@ -0,0 +1,46 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Controls
|
||||
|
||||
DialogButtonBox {
|
||||
id: root
|
||||
padding: DialogValues.popupDialogPadding
|
||||
alignment: Qt.AlignRight | Qt.AlignBottom
|
||||
|
||||
background: Rectangle {
|
||||
implicitHeight: 40
|
||||
x: 1
|
||||
y: 1
|
||||
width: parent.width - 2
|
||||
height: parent.height - 2
|
||||
color: DialogValues.darkPaneColor
|
||||
}
|
||||
|
||||
delegate: PopupDialogButton {
|
||||
width: root.count === 1 ? root.availableWidth / 2 : undefined
|
||||
}
|
||||
}
|
@@ -23,20 +23,21 @@
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Window
|
||||
import QtQuick.Controls
|
||||
|
||||
import QtQuick
|
||||
import QtQuick.Layouts
|
||||
|
||||
import StudioControls as SC
|
||||
import StudioTheme as StudioTheme
|
||||
|
||||
import BackendApi
|
||||
|
||||
Item {
|
||||
width: DialogValues.stylesPaneWidth
|
||||
|
||||
Component.onCompleted: {
|
||||
dialogBox.stylesLoaded = true;
|
||||
BackendApi.stylesLoaded = true
|
||||
|
||||
/*
|
||||
* TODO: roleNames is called before the backend model (in the proxy class StyleModel) is
|
||||
@@ -47,7 +48,7 @@ Item {
|
||||
}
|
||||
|
||||
Component.onDestruction: {
|
||||
dialogBox.stylesLoaded = false;
|
||||
BackendApi.stylesLoaded = false
|
||||
}
|
||||
|
||||
Rectangle {
|
||||
@@ -57,116 +58,120 @@ Item {
|
||||
|
||||
Item {
|
||||
x: DialogValues.stylesPanePadding
|
||||
width: parent.width - DialogValues.stylesPanePadding * 2 + styleScrollBar.width
|
||||
width: parent.width - DialogValues.stylesPanePadding * 2
|
||||
height: parent.height
|
||||
|
||||
ColumnLayout {
|
||||
anchors.fill: parent
|
||||
spacing: 5
|
||||
Text {
|
||||
id: styleTitleText
|
||||
text: qsTr("Style")
|
||||
height: DialogValues.paneTitleTextHeight
|
||||
width: parent.width
|
||||
font.weight: Font.DemiBold
|
||||
font.pixelSize: DialogValues.paneTitlePixelSize
|
||||
lineHeight: DialogValues.paneTitleLineHeight
|
||||
lineHeightMode: Text.FixedHeight
|
||||
color: DialogValues.textColor
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
anchors.top: parent.top
|
||||
anchors.left: parent.left
|
||||
anchors.right: parent.right
|
||||
|
||||
Text {
|
||||
id: styleTitleText
|
||||
text: qsTr("Style")
|
||||
Layout.minimumHeight: DialogValues.paneTitleTextHeight
|
||||
font.weight: Font.DemiBold
|
||||
font.pixelSize: DialogValues.paneTitlePixelSize
|
||||
lineHeight: DialogValues.paneTitleLineHeight
|
||||
lineHeightMode: Text.FixedHeight
|
||||
color: DialogValues.textColor
|
||||
verticalAlignment: Qt.AlignVCenter
|
||||
function refresh() {
|
||||
styleTitleText.text = qsTr("Style") + " (" + BackendApi.styleModel.rowCount() + ")"
|
||||
}
|
||||
}
|
||||
|
||||
function refresh() {
|
||||
text = qsTr("Style") + " (" + styleModel.rowCount() + ")"
|
||||
}
|
||||
SC.ComboBox { // Style Filter ComboBox
|
||||
id: styleComboBox
|
||||
actionIndicatorVisible: false
|
||||
currentIndex: 0
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
width: parent.width
|
||||
|
||||
anchors.top: styleTitleText.bottom
|
||||
anchors.topMargin: 5
|
||||
|
||||
model: ListModel {
|
||||
ListElement { text: qsTr("All"); value: "all" }
|
||||
ListElement { text: qsTr("Light"); value: "light" }
|
||||
ListElement { text: qsTr("Dark"); value: "dark" }
|
||||
}
|
||||
|
||||
SC.ComboBox { // Style Filter ComboBox
|
||||
actionIndicatorVisible: false
|
||||
currentIndex: 0
|
||||
textRole: "text"
|
||||
valueRole: "value"
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
onActivated: (index) => {
|
||||
BackendApi.styleModel.filter(currentValue.toLowerCase())
|
||||
styleTitleText.refresh()
|
||||
}
|
||||
} // Style Filter ComboBox
|
||||
|
||||
model: ListModel {
|
||||
ListElement { text: qsTr("All"); value: "all" }
|
||||
ListElement { text: qsTr("Light"); value: "light" }
|
||||
ListElement { text: qsTr("Dark"); value: "dark" }
|
||||
}
|
||||
ScrollView {
|
||||
id: scrollView
|
||||
|
||||
implicitWidth: parent.width
|
||||
anchors.top: styleComboBox.bottom
|
||||
anchors.topMargin: 11
|
||||
anchors.bottom: parent.bottom
|
||||
anchors.bottomMargin: DialogValues.stylesPanePadding
|
||||
width: parent.width
|
||||
|
||||
onActivated: (index) => {
|
||||
styleModel.filter(currentValue.toLowerCase());
|
||||
styleTitleText.refresh();
|
||||
}
|
||||
} // Style Filter ComboBox
|
||||
|
||||
Item { implicitWidth: 1; implicitHeight: 9 }
|
||||
ScrollBar.horizontal.policy: ScrollBar.AlwaysOff
|
||||
ScrollBar.vertical: SC.VerticalScrollBar {
|
||||
id: styleScrollBar
|
||||
x: stylesList.width + (DialogValues.stylesPanePadding
|
||||
- StudioTheme.Values.scrollBarThickness) * 0.5
|
||||
}
|
||||
|
||||
ListView {
|
||||
id: stylesList
|
||||
Layout.fillWidth: true
|
||||
Layout.fillHeight: true
|
||||
clip: true
|
||||
model: styleModel
|
||||
|
||||
MouseArea {
|
||||
id: listViewMouseArea
|
||||
anchors.fill: parent
|
||||
hoverEnabled: true
|
||||
propagateComposedEvents: true
|
||||
}
|
||||
|
||||
anchors.fill: parent
|
||||
focus: true
|
||||
clip: true
|
||||
model: BackendApi.styleModel
|
||||
boundsBehavior: Flickable.StopAtBounds
|
||||
|
||||
highlightFollowsCurrentItem: false
|
||||
|
||||
ScrollBar.vertical: SC.VerticalScrollBar {
|
||||
id: styleScrollBar
|
||||
property int extraPadding: 0
|
||||
bottomInset: extraPadding
|
||||
bottomPadding: bottomInset + 16
|
||||
viewMouseArea: listViewMouseArea
|
||||
} // ScrollBar
|
||||
bottomMargin: -DialogValues.styleListItemBottomMargin
|
||||
|
||||
onCurrentIndexChanged: {
|
||||
if (styleModel.rowCount() > 0)
|
||||
dialogBox.styleIndex = stylesList.currentIndex;
|
||||
if (BackendApi.styleModel.rowCount() > 0)
|
||||
BackendApi.styleIndex = stylesList.currentIndex
|
||||
}
|
||||
|
||||
delegate: ItemDelegate {
|
||||
id: delegateId
|
||||
height: styleImage.height + DialogValues.styleImageBorderWidth + styleText.height + extraPadding.height + 1
|
||||
width: stylesList.width - styleScrollBar.width
|
||||
width: stylesList.width
|
||||
height: DialogValues.styleListItemHeight
|
||||
|
||||
Component.onCompleted: {
|
||||
styleScrollBar.extraPadding = styleText.height + extraPadding.height
|
||||
}
|
||||
onClicked: stylesList.currentIndex = index
|
||||
|
||||
Rectangle {
|
||||
background: Rectangle {
|
||||
anchors.fill: parent
|
||||
color: DialogValues.lightPaneColor
|
||||
}
|
||||
|
||||
contentItem: Item {
|
||||
anchors.fill: parent
|
||||
|
||||
Column {
|
||||
spacing: 0
|
||||
anchors.fill: parent
|
||||
spacing: DialogValues.styleListItemSpacing
|
||||
|
||||
Rectangle {
|
||||
border.color: index == stylesList.currentIndex ? DialogValues.textColorInteraction : "transparent"
|
||||
border.width: index == stylesList.currentIndex ? DialogValues.styleImageBorderWidth : 0
|
||||
width: DialogValues.styleImageWidth
|
||||
+ 2 * DialogValues.styleImageBorderWidth
|
||||
height: DialogValues.styleImageHeight
|
||||
+ 2 * DialogValues.styleImageBorderWidth
|
||||
border.color: index === stylesList.currentIndex ? DialogValues.textColorInteraction : "transparent"
|
||||
border.width: index === stylesList.currentIndex ? DialogValues.styleImageBorderWidth : 0
|
||||
color: "transparent"
|
||||
width: parent.width
|
||||
height: parent.height - styleText.height - extraPadding.height
|
||||
|
||||
Image {
|
||||
id: styleImage
|
||||
asynchronous: false
|
||||
source: "image://newprojectdialog_library/" + styleModel.iconId(model.index)
|
||||
width: 200
|
||||
height: 262
|
||||
x: DialogValues.styleImageBorderWidth
|
||||
y: DialogValues.styleImageBorderWidth
|
||||
width: DialogValues.styleImageWidth
|
||||
height: DialogValues.styleImageHeight
|
||||
asynchronous: false
|
||||
source: "image://newprojectdialog_library/" + BackendApi.styleModel.iconId(model.index)
|
||||
}
|
||||
} // Rectangle
|
||||
|
||||
@@ -175,35 +180,26 @@ Item {
|
||||
text: model.display
|
||||
font.pixelSize: DialogValues.defaultPixelSize
|
||||
lineHeight: DialogValues.defaultLineHeight
|
||||
height: 18
|
||||
height: DialogValues.styleTextHeight
|
||||
lineHeightMode: Text.FixedHeight
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
width: parent.width
|
||||
color: DialogValues.textColor
|
||||
}
|
||||
|
||||
Item { id: extraPadding; width: 1; height: 10 }
|
||||
} // Column
|
||||
} // Rectangle
|
||||
|
||||
MouseArea {
|
||||
anchors.fill: parent
|
||||
onClicked: {
|
||||
stylesList.currentIndex = index
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Connections {
|
||||
target: styleModel
|
||||
target: BackendApi.styleModel
|
||||
function onModelReset() {
|
||||
stylesList.currentIndex = dialogBox.styleIndex;
|
||||
stylesList.currentIndexChanged();
|
||||
styleTitleText.refresh();
|
||||
stylesList.currentIndex = BackendApi.styleIndex
|
||||
stylesList.currentIndexChanged()
|
||||
styleTitleText.refresh()
|
||||
}
|
||||
}
|
||||
} // ListView
|
||||
} // ColumnLayout
|
||||
} // ScrollView
|
||||
} // Parent Item
|
||||
} // Rectangle
|
||||
}
|
||||
|
@@ -1,4 +1,7 @@
|
||||
singleton DialogValues 1.0 DialogValues.qml
|
||||
PopupDialog 1.0 PopupDialog.qml
|
||||
PopupDialogButton 1.0 PopupDialogButton.qml
|
||||
PopupDialogButtonBox 1.0 PopupDialogButtonBox.qml
|
||||
Details 1.0 Details.qml
|
||||
Styles 1.0 Styles.qml
|
||||
singleton DialogValues 1.0 DialogValues.qml
|
||||
NewProjectView 1.0 NewProjectView.qml
|
||||
Styles 1.0 Styles.qml
|
||||
|
@@ -35,37 +35,26 @@ ScrollBar {
|
||||
implicitHeight: Math.max(implicitBackgroundHeight + topInset + bottomInset,
|
||||
implicitContentHeight + topPadding + bottomPadding)
|
||||
|
||||
property bool scrollBarVisible: parent.childrenRect.height > parent.height
|
||||
// viewMouseArea: if set, the scrollbar will be visible only on hover over the view containing
|
||||
// the mouse area item.
|
||||
property MouseArea viewMouseArea: null
|
||||
|
||||
minimumSize: orientation == Qt.Horizontal ? height / width : width / height
|
||||
property bool scrollBarVisible: parent.contentHeight > scrollBar.height
|
||||
|
||||
minimumSize: scrollBar.width / scrollBar.height
|
||||
orientation: Qt.Vertical
|
||||
policy: computePolicy()
|
||||
x: parent.width - width
|
||||
y: 0
|
||||
policy: scrollBar.scrollBarVisible ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
|
||||
|
||||
height: parent.availableHeight
|
||||
- (parent.bothVisible ? parent.horizontalThickness : 0)
|
||||
padding: 0
|
||||
padding: scrollBar.active ? StudioTheme.Values.scrollBarActivePadding
|
||||
: StudioTheme.Values.scrollBarInactivePadding
|
||||
|
||||
background: Rectangle {
|
||||
implicitWidth: StudioTheme.Values.scrollBarThickness
|
||||
implicitHeight: StudioTheme.Values.scrollBarThickness
|
||||
color: StudioTheme.Values.themeScrollBarTrack
|
||||
}
|
||||
|
||||
contentItem: Rectangle {
|
||||
implicitWidth: StudioTheme.Values.scrollBarThickness
|
||||
implicitWidth: StudioTheme.Values.scrollBarThickness - 2 * scrollBar.padding
|
||||
implicitHeight: StudioTheme.Values.scrollBarThickness - 2 * scrollBar.padding
|
||||
color: StudioTheme.Values.themeScrollBarHandle
|
||||
}
|
||||
|
||||
function computePolicy() {
|
||||
if (!scrollBar.scrollBarVisible)
|
||||
return ScrollBar.AlwaysOff;
|
||||
|
||||
if (scrollBar.viewMouseArea)
|
||||
return scrollBar.viewMouseArea.containsMouse ? ScrollBar.AlwaysOn : ScrollBar.AlwaysOff
|
||||
else
|
||||
return ScrollBar.AlwaysOn;
|
||||
}
|
||||
}
|
||||
|
@@ -89,6 +89,8 @@ QtObject {
|
||||
property real typeLabelVerticalShift: Math.round(6 * values.scaleFactor)
|
||||
|
||||
property real scrollBarThickness: 10
|
||||
property real scrollBarActivePadding: 1
|
||||
property real scrollBarInactivePadding: 2
|
||||
|
||||
property real toolTipHeight: 25
|
||||
property int toolTipDelay: 1000
|
||||
|
@@ -14,6 +14,7 @@ add_qtc_plugin(StudioWelcome
|
||||
createproject.cpp createproject.h
|
||||
wizardhandler.cpp wizardhandler.h
|
||||
recentpresets.cpp recentpresets.h
|
||||
userpresets.cpp userpresets.h
|
||||
screensizemodel.h
|
||||
algorithm.h
|
||||
stylemodel.h stylemodel.cpp
|
||||
|
@@ -25,22 +25,30 @@
|
||||
|
||||
#include "presetmodel.h"
|
||||
#include <utils/optional.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include "algorithm.h"
|
||||
|
||||
using namespace StudioWelcome;
|
||||
|
||||
constexpr int NameRole = Qt::UserRole;
|
||||
constexpr int ScreenSizeRole = Qt::UserRole + 1;
|
||||
constexpr int IsUserPresetRole = Qt::UserRole + 2;
|
||||
|
||||
static const QString RecentsTabName = QObject::tr("Recents");
|
||||
static const QString CustomTabName = QObject::tr("Custom");
|
||||
|
||||
/****************** PresetData ******************/
|
||||
|
||||
void PresetData::setData(const PresetsByCategory &presetsByCategory,
|
||||
const std::vector<RecentPreset> &loadedRecents)
|
||||
const std::vector<UserPresetData> &userPresetsData,
|
||||
const std::vector<RecentPresetData> &loadedRecentsData)
|
||||
{
|
||||
QTC_ASSERT(!presetsByCategory.empty(), return);
|
||||
m_recents = loadedRecents;
|
||||
QTC_ASSERT(!presetsByCategory.empty(), return );
|
||||
m_recents = loadedRecentsData;
|
||||
m_userPresets = userPresetsData;
|
||||
|
||||
if (!m_recents.empty()) {
|
||||
m_categories.push_back("Recents");
|
||||
m_categories.push_back(RecentsTabName);
|
||||
m_presets.push_back({});
|
||||
}
|
||||
|
||||
@@ -49,28 +57,95 @@ void PresetData::setData(const PresetsByCategory &presetsByCategory,
|
||||
m_presets.push_back(category.items);
|
||||
}
|
||||
|
||||
PresetItems presets = Utils::flatten(m_presets);
|
||||
PresetItems wizardPresets = Utils::flatten(m_presets);
|
||||
|
||||
std::vector<PresetItem> recentPresets = makeRecentPresets(presets);
|
||||
|
||||
if (!m_recents.empty())
|
||||
PresetItems recentPresets = makeRecentPresets(wizardPresets);
|
||||
if (!m_recents.empty()) {
|
||||
m_presets[0] = recentPresets;
|
||||
}
|
||||
|
||||
PresetItems userPresetItems = makeUserPresets(wizardPresets);
|
||||
if (!userPresetItems.empty()) {
|
||||
m_categories.push_back(CustomTabName);
|
||||
m_presets.push_back(userPresetItems);
|
||||
}
|
||||
|
||||
m_presetsByCategory = presetsByCategory;
|
||||
}
|
||||
|
||||
std::vector<PresetItem> PresetData::makeRecentPresets(const PresetItems &wizardPresets)
|
||||
void PresetData::reload(const std::vector<UserPresetData> &userPresetsData,
|
||||
const std::vector<RecentPresetData> &loadedRecentsData)
|
||||
{
|
||||
static const PresetItem empty;
|
||||
m_categories.clear();
|
||||
m_presets.clear();
|
||||
m_recents.clear();
|
||||
m_userPresets.clear();
|
||||
setData(m_presetsByCategory, userPresetsData, loadedRecentsData);
|
||||
}
|
||||
|
||||
std::shared_ptr<PresetItem> PresetData::findPresetItemForUserPreset(const UserPresetData &preset,
|
||||
const PresetItems &wizardPresets)
|
||||
{
|
||||
return Utils::findOrDefault(wizardPresets, [&preset](const std::shared_ptr<PresetItem> &item) {
|
||||
return item->wizardName == preset.wizardName && item->categoryId == preset.categoryId;
|
||||
});
|
||||
}
|
||||
|
||||
PresetItems PresetData::makeUserPresets(const PresetItems &wizardPresets)
|
||||
{
|
||||
PresetItems result;
|
||||
|
||||
for (const RecentPreset &recent : m_recents) {
|
||||
auto item = Utils::findOptional(wizardPresets, [&recent](const PresetItem &item) {
|
||||
return item.categoryId == std::get<0>(recent) && item.name == std::get<1>(recent);
|
||||
});
|
||||
for (const UserPresetData &userPresetData : m_userPresets) {
|
||||
std::shared_ptr<PresetItem> foundPreset = findPresetItemForUserPreset(userPresetData,
|
||||
wizardPresets);
|
||||
if (!foundPreset)
|
||||
continue;
|
||||
|
||||
if (item) {
|
||||
item->screenSizeName = std::get<2>(recent);
|
||||
result.push_back(item.value());
|
||||
auto presetItem = std::make_shared<UserPresetItem>();
|
||||
|
||||
presetItem->categoryId = userPresetData.categoryId;
|
||||
presetItem->wizardName = userPresetData.wizardName;
|
||||
presetItem->screenSizeName = userPresetData.screenSize;
|
||||
|
||||
presetItem->userName = userPresetData.name;
|
||||
presetItem->qtVersion = userPresetData.qtVersion;
|
||||
presetItem->styleName = userPresetData.styleName;
|
||||
presetItem->useQtVirtualKeyboard = userPresetData.useQtVirtualKeyboard;
|
||||
|
||||
presetItem->create = foundPreset->create;
|
||||
presetItem->description = foundPreset->description;
|
||||
presetItem->fontIconCode = foundPreset->fontIconCode;
|
||||
presetItem->qmlPath = foundPreset->qmlPath;
|
||||
|
||||
result.push_back(presetItem);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::shared_ptr<PresetItem> PresetData::findPresetItemForRecent(const RecentPresetData &recent, const PresetItems &wizardPresets)
|
||||
{
|
||||
return Utils::findOrDefault(wizardPresets, [&recent](const std::shared_ptr<PresetItem> &item) {
|
||||
bool sameName = item->categoryId == recent.category
|
||||
&& item->displayName() == recent.presetName;
|
||||
|
||||
bool sameType = (recent.isUserPreset ? item->isUserPreset() : !item->isUserPreset());
|
||||
|
||||
return sameName && sameType;
|
||||
});
|
||||
}
|
||||
|
||||
PresetItems PresetData::makeRecentPresets(const PresetItems &wizardPresets)
|
||||
{
|
||||
PresetItems result;
|
||||
|
||||
for (const RecentPresetData &recent : m_recents) {
|
||||
std::shared_ptr<PresetItem> preset = findPresetItemForRecent(recent, wizardPresets);
|
||||
|
||||
if (preset) {
|
||||
auto clone = std::shared_ptr<PresetItem>{preset->clone()};
|
||||
clone->screenSizeName = recent.sizeName;
|
||||
result.push_back(clone);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,8 +161,8 @@ BasePresetModel::BasePresetModel(const PresetData *data, QObject *parent)
|
||||
|
||||
QHash<int, QByteArray> BasePresetModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roleNames;
|
||||
roleNames[Qt::UserRole] = "name";
|
||||
static QHash<int, QByteArray> roleNames{{NameRole, "name"},
|
||||
{ScreenSizeRole, "resolution"}};
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
@@ -97,8 +172,9 @@ PresetCategoryModel::PresetCategoryModel(const PresetData *data, QObject *parent
|
||||
: BasePresetModel(data, parent)
|
||||
{}
|
||||
|
||||
int PresetCategoryModel::rowCount(const QModelIndex &) const
|
||||
int PresetCategoryModel::rowCount(const QModelIndex &parent) const
|
||||
{
|
||||
Q_UNUSED(parent)
|
||||
return static_cast<int>(m_data->categories().size());
|
||||
}
|
||||
|
||||
@@ -116,9 +192,9 @@ PresetModel::PresetModel(const PresetData *data, QObject *parent)
|
||||
|
||||
QHash<int, QByteArray> PresetModel::roleNames() const
|
||||
{
|
||||
QHash<int, QByteArray> roleNames;
|
||||
roleNames[Qt::UserRole] = "name";
|
||||
roleNames[Qt::UserRole + 1] = "size";
|
||||
static QHash<int, QByteArray> roleNames{{NameRole, "name"},
|
||||
{ScreenSizeRole, "resolution"},
|
||||
{IsUserPresetRole, "isUserPreset"}};
|
||||
return roleNames;
|
||||
}
|
||||
|
||||
@@ -132,9 +208,16 @@ int PresetModel::rowCount(const QModelIndex &) const
|
||||
|
||||
QVariant PresetModel::data(const QModelIndex &index, int role) const
|
||||
{
|
||||
Q_UNUSED(role)
|
||||
PresetItem preset = presetsOfCurrentCategory().at(index.row());
|
||||
return QVariant::fromValue<QString>(preset.name + "\n" + preset.screenSizeName);
|
||||
std::shared_ptr<PresetItem> preset = presetsOfCurrentCategory().at(index.row());
|
||||
|
||||
if (role == NameRole)
|
||||
return preset->displayName();
|
||||
else if (role == ScreenSizeRole)
|
||||
return preset->screenSize();
|
||||
else if (role == IsUserPresetRole)
|
||||
return preset->isUserPreset();
|
||||
else
|
||||
return {};
|
||||
}
|
||||
|
||||
void PresetModel::setPage(int index)
|
||||
@@ -148,7 +231,7 @@ void PresetModel::setPage(int index)
|
||||
|
||||
QString PresetModel::fontIconCode(int index) const
|
||||
{
|
||||
Utils::optional<PresetItem> presetItem = preset(index);
|
||||
std::shared_ptr<PresetItem> presetItem = preset(index);
|
||||
if (!presetItem)
|
||||
return {};
|
||||
|
||||
|
@@ -31,8 +31,10 @@
|
||||
|
||||
#include <utils/filepath.h>
|
||||
#include <utils/optional.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
#include "recentpresets.h"
|
||||
#include "userpresets.h"
|
||||
|
||||
namespace Utils {
|
||||
class Wizard;
|
||||
@@ -40,38 +42,115 @@ class Wizard;
|
||||
|
||||
namespace StudioWelcome {
|
||||
|
||||
struct UserPresetItem;
|
||||
|
||||
struct PresetItem
|
||||
{
|
||||
QString name;
|
||||
PresetItem() = default;
|
||||
PresetItem(const QString &wizardName, const QString &category, const QString &sizeName = "")
|
||||
: wizardName{wizardName}
|
||||
, categoryId{category}
|
||||
, screenSizeName{sizeName}
|
||||
{}
|
||||
|
||||
virtual ~PresetItem() {}
|
||||
virtual QString displayName() const { return wizardName; }
|
||||
virtual QString screenSize() const { return screenSizeName; }
|
||||
virtual std::unique_ptr<PresetItem> clone() const
|
||||
{
|
||||
return std::unique_ptr<PresetItem>{new PresetItem{*this}};
|
||||
}
|
||||
|
||||
virtual bool isUserPreset() const { return false; }
|
||||
virtual UserPresetItem *asUserPreset() { return nullptr; }
|
||||
std::function<Utils::Wizard *(const Utils::FilePath &path)> create;
|
||||
|
||||
public:
|
||||
QString wizardName;
|
||||
QString categoryId;
|
||||
QString screenSizeName;
|
||||
QString description;
|
||||
QUrl qmlPath;
|
||||
QString fontIconCode;
|
||||
std::function<Utils::Wizard *(const Utils::FilePath &path)> create;
|
||||
};
|
||||
|
||||
struct UserPresetItem : public PresetItem
|
||||
{
|
||||
UserPresetItem() = default;
|
||||
UserPresetItem(const QString &userName,
|
||||
const QString &wizardName,
|
||||
const QString &category,
|
||||
const QString &sizeName = "")
|
||||
: PresetItem{wizardName, category, sizeName}
|
||||
, userName{userName}
|
||||
{}
|
||||
|
||||
QString displayName() const override { return userName; }
|
||||
std::unique_ptr<PresetItem> clone() const override
|
||||
{
|
||||
return std::unique_ptr<PresetItem>{new UserPresetItem{*this}};
|
||||
}
|
||||
|
||||
bool isUserPreset() const override { return true; }
|
||||
UserPresetItem *asUserPreset() override { return this; }
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !categoryId.isEmpty() && !wizardName.isEmpty() && !userName.isEmpty();
|
||||
}
|
||||
|
||||
public:
|
||||
QString userName;
|
||||
bool useQtVirtualKeyboard;
|
||||
QString qtVersion;
|
||||
QString styleName;
|
||||
};
|
||||
|
||||
inline QDebug &operator<<(QDebug &d, const UserPresetItem &item);
|
||||
|
||||
inline QDebug &operator<<(QDebug &d, const PresetItem &item)
|
||||
{
|
||||
d << "name=" << item.name;
|
||||
d << "wizardName=" << item.wizardName;
|
||||
d << "; category = " << item.categoryId;
|
||||
d << "; size = " << item.screenSizeName;
|
||||
|
||||
if (item.isUserPreset())
|
||||
d << (UserPresetItem &) item;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
inline QDebug &operator<<(QDebug &d, const UserPresetItem &item)
|
||||
{
|
||||
d << "userName=" << item.userName;
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
inline bool operator==(const PresetItem &lhs, const PresetItem &rhs)
|
||||
{
|
||||
return lhs.categoryId == rhs.categoryId && lhs.name == rhs.name;
|
||||
return lhs.categoryId == rhs.categoryId && lhs.wizardName == rhs.wizardName;
|
||||
}
|
||||
|
||||
using PresetItems = std::vector<std::shared_ptr<PresetItem>>;
|
||||
|
||||
struct WizardCategory
|
||||
{
|
||||
QString id;
|
||||
QString name;
|
||||
std::vector<PresetItem> items;
|
||||
PresetItems items;
|
||||
};
|
||||
|
||||
inline QDebug &operator<<(QDebug &d, const std::shared_ptr<PresetItem> &preset)
|
||||
{
|
||||
if (preset)
|
||||
d << *preset;
|
||||
else
|
||||
d << "(null)";
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
inline QDebug &operator<<(QDebug &d, const WizardCategory &cat)
|
||||
{
|
||||
d << "id=" << cat.id;
|
||||
@@ -82,7 +161,6 @@ inline QDebug &operator<<(QDebug &d, const WizardCategory &cat)
|
||||
}
|
||||
|
||||
using PresetsByCategory = std::map<QString, WizardCategory>;
|
||||
using PresetItems = std::vector<PresetItem>;
|
||||
using Categories = std::vector<QString>;
|
||||
|
||||
/****************** PresetData ******************/
|
||||
@@ -90,18 +168,28 @@ using Categories = std::vector<QString>;
|
||||
class PresetData
|
||||
{
|
||||
public:
|
||||
void setData(const PresetsByCategory &presets, const std::vector<RecentPreset> &recents);
|
||||
void reload(const std::vector<UserPresetData> &userPresets,
|
||||
const std::vector<RecentPresetData> &loadedRecents);
|
||||
void setData(const PresetsByCategory &presets,
|
||||
const std::vector<UserPresetData> &userPresets,
|
||||
const std::vector<RecentPresetData> &recents);
|
||||
|
||||
const std::vector<PresetItems> &presets() const { return m_presets; }
|
||||
const Categories &categories() const { return m_categories; }
|
||||
|
||||
private:
|
||||
std::vector<PresetItem> makeRecentPresets(const PresetItems &wizardPresets);
|
||||
PresetItems makeRecentPresets(const PresetItems &wizardPresets);
|
||||
PresetItems makeUserPresets(const PresetItems &wizardPresets);
|
||||
|
||||
std::shared_ptr<PresetItem> findPresetItemForUserPreset(const UserPresetData &preset, const PresetItems &wizardPresets);
|
||||
std::shared_ptr<PresetItem> findPresetItemForRecent(const RecentPresetData &recent, const PresetItems &wizardPresets);
|
||||
|
||||
private:
|
||||
std::vector<PresetItems> m_presets;
|
||||
Categories m_categories;
|
||||
std::vector<RecentPreset> m_recents;
|
||||
std::vector<RecentPresetData> m_recents;
|
||||
std::vector<UserPresetData> m_userPresets;
|
||||
PresetsByCategory m_presetsByCategory;
|
||||
};
|
||||
|
||||
/****************** PresetCategoryModel ******************/
|
||||
@@ -149,14 +237,14 @@ public:
|
||||
|
||||
int page() const { return static_cast<int>(m_page); }
|
||||
|
||||
Utils::optional<PresetItem> preset(size_t selection) const
|
||||
const std::shared_ptr<PresetItem> preset(size_t selection) const
|
||||
{
|
||||
auto presets = m_data->presets();
|
||||
if (presets.empty())
|
||||
return {};
|
||||
|
||||
if (m_page < presets.size()) {
|
||||
const std::vector<PresetItem> presetsOfCategory = presets.at(m_page);
|
||||
const PresetItems presetsOfCategory = presets.at(m_page);
|
||||
if (selection < presetsOfCategory.size())
|
||||
return presets.at(m_page).at(selection);
|
||||
}
|
||||
@@ -166,8 +254,10 @@ public:
|
||||
bool empty() const { return m_data->presets().empty(); }
|
||||
|
||||
private:
|
||||
const std::vector<PresetItem> presetsOfCurrentCategory() const
|
||||
const PresetItems presetsOfCurrentCategory() const
|
||||
{
|
||||
QTC_ASSERT(m_page < m_data->presets().size(), return {});
|
||||
|
||||
return m_data->presets().at(m_page);
|
||||
}
|
||||
|
||||
|
@@ -76,22 +76,14 @@ QdsNewDialog::QdsNewDialog(QWidget *parent)
|
||||
{
|
||||
setParent(m_dialog);
|
||||
|
||||
m_dialog->rootContext()->setContextProperties(QVector<QQmlContext::PropertyPair>{
|
||||
{{"categoryModel"}, QVariant::fromValue(m_categoryModel.data())},
|
||||
{{"presetModel"}, QVariant::fromValue(m_presetModel.data())},
|
||||
{{"screenSizeModel"}, QVariant::fromValue(m_screenSizeModel.data())},
|
||||
{{"styleModel"}, QVariant::fromValue(m_styleModel.data())},
|
||||
{{"dialogBox"}, QVariant::fromValue(this)},
|
||||
});
|
||||
|
||||
m_dialog->setResizeMode(QQuickWidget::SizeRootObjectToView); // SizeViewToRootObject
|
||||
m_dialog->engine()->addImageProvider(QStringLiteral("newprojectdialog_library"),
|
||||
new Internal::NewProjectDialogImageProvider());
|
||||
QmlDesigner::Theme::setupTheme(m_dialog->engine());
|
||||
qmlRegisterSingletonInstance<QdsNewDialog>("BackendApi", 1, 0, "BackendApi", this);
|
||||
m_dialog->engine()->addImportPath(Core::ICore::resourcePath("qmldesigner/propertyEditorQmlSources/imports").toString());
|
||||
m_dialog->engine()->addImportPath(Core::ICore::resourcePath("qmldesigner/newprojectdialog/imports").toString());
|
||||
QString sourcesPath = qmlPath();
|
||||
m_dialog->setSource(QUrl::fromLocalFile(sourcesPath));
|
||||
m_dialog->setSource(QUrl::fromLocalFile(qmlPath()));
|
||||
|
||||
m_dialog->setWindowModality(Qt::ApplicationModal);
|
||||
m_dialog->setWindowFlags(Qt::Dialog);
|
||||
@@ -114,7 +106,7 @@ QdsNewDialog::QdsNewDialog(QWidget *parent)
|
||||
});
|
||||
|
||||
QObject::connect(m_styleModel.data(), &StyleModel::modelAboutToBeReset, this, [this]() {
|
||||
this->m_qmlStyleIndex = -1;
|
||||
m_qmlStyleIndex = -1;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -188,18 +180,47 @@ void QdsNewDialog::onWizardCreated(QStandardItemModel *screenSizeModel, QStandar
|
||||
m_screenSizeModel->setBackendModel(screenSizeModel);
|
||||
m_styleModel->setBackendModel(styleModel);
|
||||
|
||||
auto userPreset = m_currentPreset->asUserPreset();
|
||||
|
||||
if (m_qmlDetailsLoaded) {
|
||||
updateScreenSizes();
|
||||
if (m_currentPreset->isUserPreset()) {
|
||||
if (m_wizard.haveVirtualKeyboard())
|
||||
setUseVirtualKeyboard(userPreset->useQtVirtualKeyboard);
|
||||
|
||||
if (m_wizard.haveTargetQtVersion()) {
|
||||
int index = m_wizard.targetQtVersionIndex(userPreset->qtVersion);
|
||||
if (index != -1)
|
||||
setTargetQtVersionIndex(index);
|
||||
}
|
||||
} else {
|
||||
if (m_wizard.haveTargetQtVersion()) {
|
||||
int index = m_wizard.targetQtVersionIndex();
|
||||
if (index != -1)
|
||||
setTargetQtVersionIndex(index);
|
||||
}
|
||||
}
|
||||
|
||||
emit haveVirtualKeyboardChanged();
|
||||
emit haveTargetQtVersionChanged();
|
||||
|
||||
updateScreenSizes();
|
||||
|
||||
setProjectName(m_qmlProjectName);
|
||||
setProjectLocation(m_qmlProjectLocation.toString());
|
||||
}
|
||||
|
||||
if (m_qmlStylesLoaded)
|
||||
if (m_qmlStylesLoaded && m_wizard.haveStyleModel()) {
|
||||
if (m_currentPreset->isUserPreset()) {
|
||||
int index = m_wizard.styleIndex(userPreset->styleName);
|
||||
if (index != -1)
|
||||
setStyleIndex(index);
|
||||
} else {
|
||||
/* NOTE: For a builtin preset, we don't need to set style index. That's because defaults
|
||||
* will be loaded from the backend Wizard.
|
||||
*/
|
||||
}
|
||||
m_styleModel->reset();
|
||||
}
|
||||
}
|
||||
|
||||
QString QdsNewDialog::currentPresetQmlPath() const
|
||||
@@ -221,10 +242,19 @@ int QdsNewDialog::screenSizeIndex() const
|
||||
return m_wizard.screenSizeIndex();
|
||||
}
|
||||
|
||||
void QdsNewDialog::setTargetQtVersion(int index)
|
||||
void QdsNewDialog::setTargetQtVersionIndex(int index)
|
||||
{
|
||||
m_wizard.setTargetQtVersionIndex(index);
|
||||
m_qmlTargetQtVersionIndex = index;
|
||||
if (m_qmlTargetQtVersionIndex != index) {
|
||||
m_wizard.setTargetQtVersionIndex(index);
|
||||
m_qmlTargetQtVersionIndex = index;
|
||||
|
||||
emit targetQtVersionIndexChanged();
|
||||
}
|
||||
}
|
||||
|
||||
int QdsNewDialog::getTargetQtVersionIndex() const
|
||||
{
|
||||
return m_qmlTargetQtVersionIndex;
|
||||
}
|
||||
|
||||
void QdsNewDialog::setStyleIndex(int index)
|
||||
@@ -267,6 +297,14 @@ int QdsNewDialog::getStyleIndex() const
|
||||
return m_styleModel->actualIndex(m_qmlStyleIndex);
|
||||
}
|
||||
|
||||
void QdsNewDialog::setUseVirtualKeyboard(bool value)
|
||||
{
|
||||
if (m_qmlUseVirtualKeyboard != value) {
|
||||
m_qmlUseVirtualKeyboard = value;
|
||||
emit useVirtualKeyboardChanged();
|
||||
}
|
||||
}
|
||||
|
||||
void QdsNewDialog::setWizardFactories(QList<Core::IWizardFactory *> factories_,
|
||||
const Utils::FilePath &defaultLocation,
|
||||
const QVariantMap &)
|
||||
@@ -275,8 +313,9 @@ void QdsNewDialog::setWizardFactories(QList<Core::IWizardFactory *> factories_,
|
||||
|
||||
WizardFactories factories{factories_, m_dialog, platform};
|
||||
|
||||
std::vector<RecentPreset> recents = m_recentsStore.fetchAll();
|
||||
m_presetData.setData(factories.presetsGroupedByCategory(), recents);
|
||||
std::vector<RecentPresetData> recents = m_recentsStore.fetchAll();
|
||||
std::vector<UserPresetData> userPresets = m_userPresetsStore.fetchAll();
|
||||
m_presetData.setData(factories.presetsGroupedByCategory(), userPresets, recents);
|
||||
|
||||
m_categoryModel->reset();
|
||||
m_presetModel->reset();
|
||||
@@ -296,12 +335,45 @@ void QdsNewDialog::setWizardFactories(QList<Core::IWizardFactory *> factories_,
|
||||
m_qmlProjectLocation = Utils::FilePath::fromString(QDir::toNativeSeparators(projectLocation.toString()));
|
||||
emit projectLocationChanged(); // So that QML knows to update the field
|
||||
|
||||
/* NOTE:
|
||||
* Here we expect that details are loaded && that styles are loaded. We use the
|
||||
* functionality below to update the state of the first item that is selected right when
|
||||
* the dialog pops up. Otherwise, relying solely on onWizardCreated is not useful, since
|
||||
* for the dialog popup, that wizard is created before details & styles are loaded.
|
||||
*
|
||||
* It might be a better alternative to receive notifications from QML in the cpp file, that
|
||||
* style is loaded, and that details is loaded, and do updates from there. But, if we handle
|
||||
* those events, they may be called before the wizard is created - so we would need to make
|
||||
* sure that all events have occurred before we go ahead and configure the wizard.
|
||||
*/
|
||||
|
||||
auto userPreset = m_currentPreset->asUserPreset();
|
||||
|
||||
if (m_qmlDetailsLoaded) {
|
||||
updateScreenSizes();
|
||||
|
||||
if (m_wizard.haveTargetQtVersion()) {
|
||||
int index = (userPreset ? m_wizard.targetQtVersionIndex(userPreset->qtVersion)
|
||||
: m_wizard.targetQtVersionIndex());
|
||||
if (index != -1)
|
||||
setTargetQtVersionIndex(index);
|
||||
}
|
||||
|
||||
if (m_wizard.haveVirtualKeyboard() && userPreset)
|
||||
setUseVirtualKeyboard(userPreset->useQtVirtualKeyboard);
|
||||
|
||||
emit haveVirtualKeyboardChanged();
|
||||
emit haveTargetQtVersionChanged();
|
||||
}
|
||||
|
||||
if (m_qmlStylesLoaded)
|
||||
if (m_qmlStylesLoaded && m_wizard.haveStyleModel()) {
|
||||
if (userPreset) {
|
||||
int index = m_wizard.styleIndex(userPreset->styleName);
|
||||
if (index != -1)
|
||||
setStyleIndex(index);
|
||||
}
|
||||
m_styleModel->reset();
|
||||
}
|
||||
}
|
||||
|
||||
QString QdsNewDialog::qmlPath() const
|
||||
@@ -338,10 +410,10 @@ void QdsNewDialog::accept()
|
||||
.withTargetQtVersion(m_qmlTargetQtVersionIndex)
|
||||
.execute();
|
||||
|
||||
PresetItem item = m_wizard.preset();
|
||||
std::shared_ptr<PresetItem> item = m_wizard.preset();
|
||||
QString customSizeName = m_qmlCustomWidth + " x " + m_qmlCustomHeight;
|
||||
|
||||
m_recentsStore.add(item.categoryId, item.name, customSizeName);
|
||||
m_recentsStore.add(item->categoryId, item->displayName(), customSizeName, item->isUserPreset());
|
||||
|
||||
m_dialog->close();
|
||||
m_dialog->deleteLater();
|
||||
@@ -355,6 +427,7 @@ void QdsNewDialog::reject()
|
||||
m_wizard.destroyWizard();
|
||||
|
||||
m_dialog->close();
|
||||
m_dialog = nullptr;
|
||||
}
|
||||
|
||||
QString QdsNewDialog::chooseProjectLocation()
|
||||
@@ -375,7 +448,76 @@ void QdsNewDialog::setSelectedPreset(int selection)
|
||||
setProjectDescription(m_currentPreset->description);
|
||||
|
||||
m_presetPage = m_presetModel->page();
|
||||
m_wizard.reset(m_currentPreset.value(), m_qmlSelectedPreset);
|
||||
m_wizard.reset(m_currentPreset, m_qmlSelectedPreset);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void QdsNewDialog::savePresetDialogAccept()
|
||||
{
|
||||
QString screenSize = m_qmlCustomWidth + " x " + m_qmlCustomHeight;
|
||||
QString targetQtVersion = "";
|
||||
QString styleName = "";
|
||||
bool useVirtualKeyboard = false;
|
||||
|
||||
if (m_wizard.haveTargetQtVersion())
|
||||
targetQtVersion = m_wizard.targetQtVersionName(m_qmlTargetQtVersionIndex);
|
||||
|
||||
if (m_wizard.haveStyleModel())
|
||||
styleName = m_wizard.styleName(m_qmlStyleIndex);
|
||||
|
||||
if (m_wizard.haveVirtualKeyboard())
|
||||
useVirtualKeyboard = m_qmlUseVirtualKeyboard;
|
||||
|
||||
UserPresetData preset = {m_currentPreset->categoryId,
|
||||
m_currentPreset->wizardName,
|
||||
m_qmlPresetName,
|
||||
screenSize,
|
||||
useVirtualKeyboard,
|
||||
targetQtVersion,
|
||||
styleName};
|
||||
|
||||
if (!m_userPresetsStore.save(preset)) {
|
||||
QMessageBox::warning(m_dialog,
|
||||
tr("Save Preset"),
|
||||
tr("A preset with this name already exists."));
|
||||
return;
|
||||
}
|
||||
|
||||
// reload model
|
||||
std::vector<RecentPresetData> recents = m_recentsStore.fetchAll();
|
||||
std::vector<UserPresetData> userPresets = m_userPresetsStore.fetchAll();
|
||||
m_presetData.reload(userPresets, recents);
|
||||
|
||||
m_categoryModel->reset();
|
||||
|
||||
emit userPresetSaved();
|
||||
}
|
||||
|
||||
void QdsNewDialog::removeCurrentPreset()
|
||||
{
|
||||
if (!m_currentPreset->isUserPreset()) {
|
||||
qWarning() << "Will not attempt to remove non-user preset";
|
||||
return;
|
||||
}
|
||||
|
||||
// remove preset & reload model
|
||||
std::vector<RecentPresetData> recents = m_recentsStore.remove(m_currentPreset->categoryId,
|
||||
m_currentPreset->displayName());
|
||||
|
||||
auto userPreset = m_currentPreset->asUserPreset();
|
||||
m_userPresetsStore.remove(userPreset->categoryId, userPreset->displayName());
|
||||
std::vector<UserPresetData> userPresets = m_userPresetsStore.fetchAll();
|
||||
m_presetData.reload(userPresets, recents);
|
||||
|
||||
m_qmlSelectedPreset = -1;
|
||||
m_presetPage = -1;
|
||||
|
||||
if (userPresets.size() == 0) {
|
||||
m_presetModel->setPage(0);
|
||||
emit lastUserPresetRemoved();
|
||||
}
|
||||
|
||||
m_categoryModel->reset();
|
||||
m_presetModel->reset();
|
||||
}
|
||||
|
@@ -29,13 +29,13 @@
|
||||
|
||||
#include <coreplugin/dialogs/newdialog.h>
|
||||
#include <utils/infolabel.h>
|
||||
#include <utils/optional.h>
|
||||
|
||||
#include "wizardhandler.h"
|
||||
#include "presetmodel.h"
|
||||
#include "screensizemodel.h"
|
||||
#include "stylemodel.h"
|
||||
#include "recentpresets.h"
|
||||
#include "userpresets.h"
|
||||
|
||||
QT_BEGIN_NAMESPACE
|
||||
class QStandardItemModel;
|
||||
@@ -57,22 +57,31 @@ public:
|
||||
Q_PROPERTY(bool useVirtualKeyboard MEMBER m_qmlUseVirtualKeyboard READ getUseVirtualKeyboard WRITE setUseVirtualKeyboard NOTIFY useVirtualKeyboardChanged)
|
||||
Q_PROPERTY(bool haveVirtualKeyboard MEMBER m_qmlHaveVirtualKeyboard READ getHaveVirtualKeyboard NOTIFY haveVirtualKeyboardChanged)
|
||||
Q_PROPERTY(bool haveTargetQtVersion MEMBER m_qmlHaveTargetQtVersion READ getHaveTargetQtVersion NOTIFY haveTargetQtVersionChanged)
|
||||
Q_PROPERTY(int targetQtVersionIndex MEMBER m_qmlTargetQtVersionIndex READ getTargetQtVersionIndex WRITE setTargetQtVersionIndex NOTIFY targetQtVersionIndexChanged)
|
||||
Q_PROPERTY(bool saveAsDefaultLocation MEMBER m_qmlSaveAsDefaultLocation WRITE setSaveAsDefaultLocation)
|
||||
Q_PROPERTY(QString statusMessage MEMBER m_qmlStatusMessage READ getStatusMessage NOTIFY statusMessageChanged)
|
||||
Q_PROPERTY(QString statusType MEMBER m_qmlStatusType READ getStatusType NOTIFY statusTypeChanged)
|
||||
Q_PROPERTY(bool fieldsValid MEMBER m_qmlFieldsValid READ getFieldsValid NOTIFY fieldsValidChanged)
|
||||
Q_PROPERTY(QString presetName MEMBER m_qmlPresetName)
|
||||
|
||||
Q_PROPERTY(bool detailsLoaded MEMBER m_qmlDetailsLoaded)
|
||||
Q_PROPERTY(bool stylesLoaded MEMBER m_qmlStylesLoaded)
|
||||
|
||||
Q_INVOKABLE void removeCurrentPreset();
|
||||
Q_INVOKABLE QString currentPresetQmlPath() const;
|
||||
// TODO: screen size index should better be a property
|
||||
Q_INVOKABLE void setScreenSizeIndex(int index); // called when ComboBox item is "activated"
|
||||
Q_INVOKABLE int screenSizeIndex() const;
|
||||
Q_INVOKABLE void setTargetQtVersion(int index);
|
||||
|
||||
Q_INVOKABLE QString chooseProjectLocation();
|
||||
|
||||
Q_PROPERTY(QAbstractListModel *categoryModel MEMBER m_categoryModel CONSTANT);
|
||||
Q_PROPERTY(QAbstractListModel *presetModel MEMBER m_presetModel CONSTANT);
|
||||
Q_PROPERTY(QAbstractListModel *screenSizeModel MEMBER m_screenSizeModel CONSTANT);
|
||||
Q_PROPERTY(QAbstractListModel *styleModel MEMBER m_styleModel CONSTANT);
|
||||
|
||||
/*********************/
|
||||
|
||||
explicit QdsNewDialog(QWidget *parent);
|
||||
|
||||
QWidget *widget() override { return m_dialog; }
|
||||
@@ -85,7 +94,11 @@ public:
|
||||
|
||||
void setStyleIndex(int index);
|
||||
int getStyleIndex() const;
|
||||
void setUseVirtualKeyboard(bool value) { m_qmlUseVirtualKeyboard = value; }
|
||||
|
||||
void setTargetQtVersionIndex(int index);
|
||||
int getTargetQtVersionIndex() const;
|
||||
|
||||
void setUseVirtualKeyboard(bool value);
|
||||
bool getUseVirtualKeyboard() const { return m_qmlUseVirtualKeyboard; }
|
||||
|
||||
bool getFieldsValid() const { return m_qmlFieldsValid; }
|
||||
@@ -101,6 +114,8 @@ public slots:
|
||||
void accept();
|
||||
void reject();
|
||||
|
||||
void savePresetDialogAccept();
|
||||
|
||||
signals:
|
||||
void projectNameChanged();
|
||||
void projectLocationChanged();
|
||||
@@ -111,6 +126,9 @@ signals:
|
||||
void statusMessageChanged();
|
||||
void statusTypeChanged();
|
||||
void fieldsValidChanged();
|
||||
void targetQtVersionIndexChanged();
|
||||
void userPresetSaved();
|
||||
void lastUserPresetRemoved();
|
||||
|
||||
private slots:
|
||||
void onStatusMessageChanged(Utils::InfoLabel::InfoType type, const QString &message);
|
||||
@@ -160,6 +178,7 @@ private:
|
||||
bool m_qmlFieldsValid = false;
|
||||
QString m_qmlStatusMessage;
|
||||
QString m_qmlStatusType;
|
||||
QString m_qmlPresetName;
|
||||
|
||||
int m_presetPage = -1; // i.e. the page in the Presets View
|
||||
|
||||
@@ -169,10 +188,11 @@ private:
|
||||
bool m_qmlDetailsLoaded = false;
|
||||
bool m_qmlStylesLoaded = false;
|
||||
|
||||
Utils::optional<PresetItem> m_currentPreset;
|
||||
std::shared_ptr<PresetItem> m_currentPreset;
|
||||
|
||||
WizardHandler m_wizard;
|
||||
RecentPresetsStore m_recentsStore;
|
||||
UserPresetsStore m_userPresetsStore;
|
||||
};
|
||||
|
||||
} //namespace StudioWelcome
|
||||
|
@@ -32,19 +32,27 @@
|
||||
#include <utils/qtcassert.h>
|
||||
#include <utils/qtcsettings.h>
|
||||
|
||||
using Core::ICore;
|
||||
using Utils::QtcSettings;
|
||||
|
||||
using namespace StudioWelcome;
|
||||
|
||||
constexpr char GROUP_NAME[] = "RecentPresets";
|
||||
constexpr char WIZARDS[] = "Wizards";
|
||||
|
||||
void RecentPresetsStore::add(const QString &categoryId, const QString &name, const QString &sizeName)
|
||||
void RecentPresetsStore::add(const QString &categoryId,
|
||||
const QString &name,
|
||||
const QString &sizeName,
|
||||
bool isUserPreset)
|
||||
{
|
||||
std::vector<RecentPreset> existing = fetchAll();
|
||||
QStringList encodedRecents = addRecentToExisting(RecentPreset{categoryId, name, sizeName},
|
||||
existing);
|
||||
std::vector<RecentPresetData> existing = fetchAll();
|
||||
|
||||
std::vector<RecentPresetData> recents
|
||||
= addRecentToExisting(RecentPresetData{categoryId, name, sizeName, isUserPreset}, existing);
|
||||
|
||||
save(recents);
|
||||
}
|
||||
|
||||
void RecentPresetsStore::save(const std::vector<RecentPresetData> &recents)
|
||||
{
|
||||
QStringList encodedRecents = encodeRecentPresets(recents);
|
||||
|
||||
m_settings->beginGroup(GROUP_NAME);
|
||||
m_settings->setValue(WIZARDS, encodedRecents);
|
||||
@@ -52,8 +60,26 @@ void RecentPresetsStore::add(const QString &categoryId, const QString &name, con
|
||||
m_settings->sync();
|
||||
}
|
||||
|
||||
QStringList RecentPresetsStore::addRecentToExisting(const RecentPreset &preset,
|
||||
std::vector<RecentPreset> &recents)
|
||||
std::vector<RecentPresetData> RecentPresetsStore::remove(const QString &categoryId, const QString &presetName)
|
||||
{
|
||||
std::vector<RecentPresetData> recents = fetchAll();
|
||||
size_t countBefore = recents.size();
|
||||
|
||||
/* NOTE: when removing one preset, it may happen that there are more than one recent for that
|
||||
* preset. In that case, we need to remove all associated recents, for the preset.*/
|
||||
|
||||
Utils::erase(recents, [&](const RecentPresetData &p) {
|
||||
return p.category == categoryId && p.presetName == presetName;
|
||||
});
|
||||
|
||||
if (recents.size() < countBefore)
|
||||
save(recents);
|
||||
|
||||
return recents;
|
||||
}
|
||||
|
||||
std::vector<RecentPresetData> RecentPresetsStore::addRecentToExisting(
|
||||
const RecentPresetData &preset, std::vector<RecentPresetData> &recents)
|
||||
{
|
||||
Utils::erase_one(recents, preset);
|
||||
Utils::prepend(recents, preset);
|
||||
@@ -61,48 +87,64 @@ QStringList RecentPresetsStore::addRecentToExisting(const RecentPreset &preset,
|
||||
if (int(recents.size()) > m_max)
|
||||
recents.pop_back();
|
||||
|
||||
return encodeRecentPresets(recents);
|
||||
return recents;
|
||||
}
|
||||
|
||||
std::vector<RecentPreset> RecentPresetsStore::fetchAll() const
|
||||
std::vector<RecentPresetData> RecentPresetsStore::fetchAll() const
|
||||
{
|
||||
m_settings->beginGroup(GROUP_NAME);
|
||||
QVariant value = m_settings->value(WIZARDS);
|
||||
m_settings->endGroup();
|
||||
|
||||
std::vector<RecentPreset> result;
|
||||
std::vector<RecentPresetData> result;
|
||||
|
||||
if (value.type() == QVariant::String)
|
||||
result.push_back(decodeOneRecentPreset(value.toString()));
|
||||
else if (value.type() == QVariant::StringList)
|
||||
Utils::concat(result, decodeRecentPresets(value.toList()));
|
||||
|
||||
const RecentPreset empty;
|
||||
return Utils::filtered(result, [&empty](const RecentPreset &recent) { return recent != empty; });
|
||||
const RecentPresetData empty;
|
||||
return Utils::filtered(result, [&empty](const RecentPresetData &recent) { return recent != empty; });
|
||||
}
|
||||
|
||||
QStringList RecentPresetsStore::encodeRecentPresets(const std::vector<RecentPreset> &recents)
|
||||
QStringList RecentPresetsStore::encodeRecentPresets(const std::vector<RecentPresetData> &recents)
|
||||
{
|
||||
return Utils::transform<QList>(recents, [](const RecentPreset &p) -> QString {
|
||||
return std::get<0>(p) + "/" + std::get<1>(p) + ":" + std::get<2>(p);
|
||||
return Utils::transform<QList>(recents, [](const RecentPresetData &p) -> QString {
|
||||
QString name = p.presetName;
|
||||
if (p.isUserPreset)
|
||||
name.prepend("[U]");
|
||||
|
||||
return p.category + "/" + name + ":" + p.sizeName;
|
||||
});
|
||||
}
|
||||
|
||||
RecentPreset RecentPresetsStore::decodeOneRecentPreset(const QString &encoded)
|
||||
RecentPresetData RecentPresetsStore::decodeOneRecentPreset(const QString &encoded)
|
||||
{
|
||||
QRegularExpression pattern{R"(^(\S+)/(.+):(\d+ x \d+))"};
|
||||
QRegularExpression pattern{R"(^(\S+)/(.+):(\d+ x \d+)$)"};
|
||||
auto m = pattern.match(encoded);
|
||||
if (!m.hasMatch())
|
||||
return RecentPreset{};
|
||||
return RecentPresetData{};
|
||||
|
||||
QString category = m.captured(1);
|
||||
QString name = m.captured(2);
|
||||
QString size = m.captured(3);
|
||||
bool isUserPreset = name.startsWith("[U]");
|
||||
if (isUserPreset)
|
||||
name = name.split("[U]")[1];
|
||||
|
||||
return std::make_tuple(category, name, size);
|
||||
if (!QRegularExpression{R"(^\w[\w ]*$)"}.match(name).hasMatch())
|
||||
return RecentPresetData{};
|
||||
|
||||
RecentPresetData result;
|
||||
result.category = category;
|
||||
result.presetName = name;
|
||||
result.sizeName = size;
|
||||
result.isUserPreset = isUserPreset;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<RecentPreset> RecentPresetsStore::decodeRecentPresets(const QVariantList &values)
|
||||
std::vector<RecentPresetData> RecentPresetsStore::decodeRecentPresets(const QVariantList &values)
|
||||
{
|
||||
return Utils::transform<std::vector>(values, [](const QVariant &value) {
|
||||
return decodeOneRecentPreset(value.toString());
|
||||
|
@@ -31,8 +31,43 @@
|
||||
|
||||
namespace StudioWelcome {
|
||||
|
||||
// preset category, preset name, size name
|
||||
using RecentPreset = std::tuple<QString, QString, QString>;
|
||||
struct RecentPresetData
|
||||
{
|
||||
RecentPresetData() = default;
|
||||
RecentPresetData(const QString &category,
|
||||
const QString &name,
|
||||
const QString &size,
|
||||
bool isUserPreset = false)
|
||||
: category{category}
|
||||
, presetName{name}
|
||||
, sizeName{size}
|
||||
, isUserPreset{isUserPreset}
|
||||
{}
|
||||
|
||||
QString category;
|
||||
QString presetName;
|
||||
QString sizeName;
|
||||
bool isUserPreset = false;
|
||||
};
|
||||
|
||||
inline bool operator==(const RecentPresetData &lhs, const RecentPresetData &rhs)
|
||||
{
|
||||
return lhs.category == rhs.category && lhs.presetName == rhs.presetName
|
||||
&& lhs.sizeName == rhs.sizeName && lhs.isUserPreset == rhs.isUserPreset;
|
||||
}
|
||||
|
||||
inline bool operator!=(const RecentPresetData &lhs, const RecentPresetData &rhs)
|
||||
{
|
||||
return !(lhs == rhs);
|
||||
}
|
||||
|
||||
inline QDebug &operator<<(QDebug &d, const RecentPresetData &preset)
|
||||
{
|
||||
d << "RecentPreset{category=" << preset.category << "; name=" << preset.presetName
|
||||
<< "; size=" << preset.sizeName << "; isUserPreset=" << preset.isUserPreset << "}";
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
class RecentPresetsStore
|
||||
{
|
||||
@@ -42,14 +77,21 @@ public:
|
||||
{}
|
||||
|
||||
void setMaximum(int n) { m_max = n; }
|
||||
void add(const QString &categoryId, const QString &name, const QString &sizeName);
|
||||
std::vector<RecentPreset> fetchAll() const;
|
||||
void add(const QString &categoryId,
|
||||
const QString &name,
|
||||
const QString &sizeName,
|
||||
bool isUserPreset = false);
|
||||
|
||||
std::vector<RecentPresetData> remove(const QString &categoryId, const QString &presetName);
|
||||
std::vector<RecentPresetData> fetchAll() const;
|
||||
|
||||
private:
|
||||
QStringList addRecentToExisting(const RecentPreset &preset, std::vector<RecentPreset> &recents);
|
||||
static QStringList encodeRecentPresets(const std::vector<RecentPreset> &recents);
|
||||
static std::vector<RecentPreset> decodeRecentPresets(const QVariantList &values);
|
||||
static RecentPreset decodeOneRecentPreset(const QString &encoded);
|
||||
std::vector<RecentPresetData> addRecentToExisting(const RecentPresetData &preset,
|
||||
std::vector<RecentPresetData> &recents);
|
||||
static QStringList encodeRecentPresets(const std::vector<RecentPresetData> &recents);
|
||||
static std::vector<RecentPresetData> decodeRecentPresets(const QVariantList &values);
|
||||
static RecentPresetData decodeOneRecentPreset(const QString &encoded);
|
||||
void save(const std::vector<RecentPresetData> &recents);
|
||||
|
||||
private:
|
||||
QSettings *m_settings = nullptr;
|
||||
|
@@ -38,7 +38,9 @@ QtcPlugin {
|
||||
"wizardhandler.cpp",
|
||||
"wizardhandler.h",
|
||||
"recentpresets.cpp",
|
||||
"recentpresets.h"
|
||||
"recentpresets.h",
|
||||
"userpresets.cpp",
|
||||
"userpresets.h"
|
||||
]
|
||||
|
||||
Group {
|
||||
|
125
src/plugins/studiowelcome/userpresets.cpp
Normal file
@@ -0,0 +1,125 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "userpresets.h"
|
||||
|
||||
#include <coreplugin/icore.h>
|
||||
#include <utils/algorithm.h>
|
||||
#include <utils/qtcassert.h>
|
||||
|
||||
using namespace StudioWelcome;
|
||||
|
||||
constexpr char PREFIX[] = "UserPresets";
|
||||
|
||||
UserPresetsStore::UserPresetsStore()
|
||||
{
|
||||
m_settings = std::make_unique<QSettings>(fullFilePath(), QSettings::IniFormat);
|
||||
}
|
||||
|
||||
UserPresetsStore::UserPresetsStore(std::unique_ptr<QSettings> &&settings)
|
||||
: m_settings{std::move(settings)}
|
||||
{}
|
||||
|
||||
void UserPresetsStore::savePresets(const std::vector<UserPresetData> &presets)
|
||||
{
|
||||
m_settings->beginWriteArray(PREFIX, static_cast<int>(presets.size()));
|
||||
|
||||
for (size_t i = 0; i < presets.size(); ++i) {
|
||||
m_settings->setArrayIndex(static_cast<int>(i));
|
||||
const auto &preset = presets[i];
|
||||
|
||||
m_settings->setValue("categoryId", preset.categoryId);
|
||||
m_settings->setValue("wizardName", preset.wizardName);
|
||||
m_settings->setValue("name", preset.name);
|
||||
m_settings->setValue("screenSize", preset.screenSize);
|
||||
m_settings->setValue("useQtVirtualKeyboard", preset.useQtVirtualKeyboard);
|
||||
m_settings->setValue("qtVersion", preset.qtVersion);
|
||||
m_settings->setValue("styleName", preset.styleName);
|
||||
}
|
||||
m_settings->endArray();
|
||||
m_settings->sync();
|
||||
|
||||
}
|
||||
|
||||
bool UserPresetsStore::save(const UserPresetData &newPreset)
|
||||
{
|
||||
QTC_ASSERT(newPreset.isValid(), return false);
|
||||
|
||||
std::vector<UserPresetData> presetItems = fetchAll();
|
||||
if (Utils::anyOf(presetItems,
|
||||
[&newPreset](const UserPresetData &p) { return p.name == newPreset.name; })) {
|
||||
return false;
|
||||
}
|
||||
|
||||
presetItems.push_back(newPreset);
|
||||
savePresets(presetItems);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UserPresetsStore::remove(const QString &category, const QString &name)
|
||||
{
|
||||
std::vector<UserPresetData> presetItems = fetchAll();
|
||||
auto item = Utils::take(presetItems, [&](const UserPresetData &p) {
|
||||
return p.categoryId == category && p.name == name;
|
||||
});
|
||||
|
||||
if (!item)
|
||||
return;
|
||||
|
||||
savePresets(presetItems);
|
||||
}
|
||||
|
||||
std::vector<UserPresetData> UserPresetsStore::fetchAll() const
|
||||
{
|
||||
std::vector<UserPresetData> result;
|
||||
int size = m_settings->beginReadArray(PREFIX);
|
||||
if (size >= 0)
|
||||
result.reserve(static_cast<size_t>(size) + 1);
|
||||
|
||||
for (int i = 0; i < size; ++i) {
|
||||
m_settings->setArrayIndex(i);
|
||||
|
||||
UserPresetData preset;
|
||||
preset.categoryId = m_settings->value("categoryId").toString();
|
||||
preset.wizardName = m_settings->value("wizardName").toString();
|
||||
preset.name = m_settings->value("name").toString();
|
||||
preset.screenSize = m_settings->value("screenSize").toString();
|
||||
preset.useQtVirtualKeyboard = m_settings->value("useQtVirtualKeyboard").toBool();
|
||||
preset.qtVersion = m_settings->value("qtVersion").toString();
|
||||
preset.styleName = m_settings->value("styleName").toString();
|
||||
|
||||
if (preset.isValid())
|
||||
result.push_back(std::move(preset));
|
||||
}
|
||||
m_settings->endArray();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
QString UserPresetsStore::fullFilePath() const
|
||||
{
|
||||
return Core::ICore::userResourcePath("UserPresets.ini").toString();
|
||||
}
|
91
src/plugins/studiowelcome/userpresets.h
Normal file
@@ -0,0 +1,91 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include <QSettings>
|
||||
|
||||
namespace StudioWelcome {
|
||||
|
||||
struct UserPresetData
|
||||
{
|
||||
QString categoryId;
|
||||
QString wizardName;
|
||||
QString name;
|
||||
QString screenSize;
|
||||
|
||||
bool useQtVirtualKeyboard;
|
||||
QString qtVersion;
|
||||
QString styleName;
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !categoryId.isEmpty()
|
||||
&& !wizardName.isEmpty()
|
||||
&& !name.isEmpty();
|
||||
}
|
||||
};
|
||||
|
||||
inline QDebug &operator<<(QDebug &d, const UserPresetData &preset)
|
||||
{
|
||||
d << "UserPreset{category = " << preset.categoryId;
|
||||
d << "; wizardName = " << preset.wizardName;
|
||||
d << "; name = " << preset.name;
|
||||
d << "; screenSize = " << preset.screenSize;
|
||||
d << "; keyboard = " << preset.useQtVirtualKeyboard;
|
||||
d << "; qt = " << preset.qtVersion;
|
||||
d << "; style = " << preset.styleName;
|
||||
d << "}";
|
||||
|
||||
return d;
|
||||
}
|
||||
|
||||
inline bool operator==(const UserPresetData &lhs, const UserPresetData &rhs)
|
||||
{
|
||||
return lhs.categoryId == rhs.categoryId && lhs.wizardName == rhs.wizardName
|
||||
&& lhs.name == rhs.name && lhs.screenSize == rhs.screenSize
|
||||
&& lhs.useQtVirtualKeyboard == rhs.useQtVirtualKeyboard && lhs.qtVersion == rhs.qtVersion
|
||||
&& lhs.styleName == rhs.styleName;
|
||||
}
|
||||
|
||||
class UserPresetsStore
|
||||
{
|
||||
public:
|
||||
UserPresetsStore();
|
||||
UserPresetsStore(std::unique_ptr<QSettings> &&settings);
|
||||
|
||||
bool save(const UserPresetData &preset);
|
||||
void remove(const QString &category, const QString &name);
|
||||
std::vector<UserPresetData> fetchAll() const;
|
||||
|
||||
private:
|
||||
QString fullFilePath() const;
|
||||
void savePresets(const std::vector<UserPresetData> &presets);
|
||||
|
||||
std::unique_ptr<QSettings> m_settings;
|
||||
};
|
||||
|
||||
} // namespace StudioWelcome
|
@@ -76,7 +76,7 @@ void WizardFactories::filter()
|
||||
m_factories = acceptedFactories;
|
||||
}
|
||||
|
||||
PresetItem WizardFactories::makePresetItem(JsonWizardFactory *f, QWidget *parent,
|
||||
std::shared_ptr<PresetItem> WizardFactories::makePresetItem(JsonWizardFactory *f, QWidget *parent,
|
||||
const Utils::Id &platform)
|
||||
{
|
||||
using namespace std::placeholders;
|
||||
@@ -89,16 +89,17 @@ PresetItem WizardFactories::makePresetItem(JsonWizardFactory *f, QWidget *parent
|
||||
else
|
||||
sizeName = screenSizes[index];
|
||||
|
||||
return {
|
||||
/*.name =*/f->displayName(),
|
||||
/*.categoryId =*/f->category(),
|
||||
/*.screenSizeName=*/sizeName,
|
||||
/*.description =*/f->description(),
|
||||
/*.qmlPath =*/f->detailsPageQmlPath(),
|
||||
/*.fontIconCode =*/m_getIconUnicode(f->fontIconName()),
|
||||
/*.create =*/ std::bind(&JsonWizardFactory::runWizard, f, _1, parent, platform,
|
||||
QVariantMap(), false),
|
||||
};
|
||||
auto result = std::make_shared<PresetItem>();
|
||||
result->wizardName = f->displayName();
|
||||
result->categoryId = f->category();
|
||||
result->screenSizeName=sizeName;
|
||||
result->description = f->description();
|
||||
result->qmlPath = f->detailsPageQmlPath();
|
||||
result->fontIconCode = m_getIconUnicode(f->fontIconName());
|
||||
result->create
|
||||
= std::bind(&JsonWizardFactory::runWizard, f, _1, parent, platform, QVariantMap(), false);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::map<QString, WizardCategory> WizardFactories::makePresetItemsGroupedByCategory()
|
||||
|
@@ -64,7 +64,7 @@ private:
|
||||
void sortByCategoryAndId();
|
||||
void filter();
|
||||
|
||||
PresetItem makePresetItem(JsonWizardFactory *f, QWidget *parent, const Utils::Id &platform);
|
||||
std::shared_ptr<PresetItem> makePresetItem(JsonWizardFactory *f, QWidget *parent, const Utils::Id &platform);
|
||||
std::map<QString, WizardCategory> makePresetItemsGroupedByCategory();
|
||||
|
||||
private:
|
||||
|
@@ -38,7 +38,7 @@
|
||||
|
||||
using namespace StudioWelcome;
|
||||
|
||||
void WizardHandler::reset(const PresetItem &presetInfo, int presetSelection)
|
||||
void WizardHandler::reset(const std::shared_ptr<PresetItem> &presetInfo, int presetSelection)
|
||||
{
|
||||
m_preset = presetInfo;
|
||||
m_selectedPreset = presetSelection;
|
||||
@@ -67,7 +67,7 @@ void WizardHandler::destroyWizard()
|
||||
|
||||
void WizardHandler::setupWizard()
|
||||
{
|
||||
m_wizard = m_preset.create(m_projectLocation);
|
||||
m_wizard = m_preset->create(m_projectLocation);
|
||||
if (!m_wizard) {
|
||||
emit wizardCreationFailed();
|
||||
return;
|
||||
@@ -142,6 +142,11 @@ QStandardItemModel *WizardHandler::getScreenFactorModel(ProjectExplorer::JsonFie
|
||||
return cbfield->model();
|
||||
}
|
||||
|
||||
bool WizardHandler::haveStyleModel() const
|
||||
{
|
||||
return m_wizard->hasField("ControlsStyle");
|
||||
}
|
||||
|
||||
QStandardItemModel *WizardHandler::getStyleModel(ProjectExplorer::JsonFieldPage *page)
|
||||
{
|
||||
auto *field = page->jsonField("ControlsStyle");
|
||||
@@ -229,6 +234,47 @@ bool WizardHandler::haveTargetQtVersion() const
|
||||
return m_wizard->hasField("TargetQtVersion");
|
||||
}
|
||||
|
||||
QString WizardHandler::targetQtVersionName(int index) const
|
||||
{
|
||||
auto *field = m_detailsPage->jsonField("TargetQtVersion");
|
||||
auto *cbfield = dynamic_cast<ProjectExplorer::ComboBoxField *>(field);
|
||||
QTC_ASSERT(cbfield, return "");
|
||||
|
||||
QStandardItemModel *model = cbfield->model();
|
||||
if (index < 0 || index >= model->rowCount())
|
||||
return {};
|
||||
|
||||
QString text = model->item(index)->text();
|
||||
return text;
|
||||
}
|
||||
|
||||
int WizardHandler::targetQtVersionIndex(const QString &qtVersionName) const
|
||||
{
|
||||
auto *field = m_detailsPage->jsonField("TargetQtVersion");
|
||||
auto *cbfield = dynamic_cast<ProjectExplorer::ComboBoxField *>(field);
|
||||
QTC_ASSERT(cbfield, return -1);
|
||||
|
||||
const QStandardItemModel *model = cbfield->model();
|
||||
for (int i = 0; i < model->rowCount(); ++i) {
|
||||
const QStandardItem *item = model->item(i, 0);
|
||||
const QString text = item->text();
|
||||
|
||||
if (text == qtVersionName)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
int WizardHandler::targetQtVersionIndex() const
|
||||
{
|
||||
auto *field = m_detailsPage->jsonField("TargetQtVersion");
|
||||
auto *cbfield = dynamic_cast<ProjectExplorer::ComboBoxField *>(field);
|
||||
QTC_ASSERT(cbfield, return -1);
|
||||
|
||||
return cbfield->selectedRow();
|
||||
}
|
||||
|
||||
void WizardHandler::setStyleIndex(int index)
|
||||
{
|
||||
auto *field = m_detailsPage->jsonField("ControlsStyle");
|
||||
@@ -247,6 +293,38 @@ int WizardHandler::styleIndex() const
|
||||
return cbfield->selectedRow();
|
||||
}
|
||||
|
||||
int WizardHandler::styleIndex(const QString &styleName) const
|
||||
{
|
||||
auto *field = m_detailsPage->jsonField("ControlsStyle");
|
||||
auto *cbfield = dynamic_cast<ProjectExplorer::ComboBoxField *>(field);
|
||||
QTC_ASSERT(cbfield, return -1);
|
||||
|
||||
const QStandardItemModel *model = cbfield->model();
|
||||
for (int i = 0; i < model->rowCount(); ++i) {
|
||||
const QStandardItem *item = model->item(i, 0);
|
||||
const QString text = item->text();
|
||||
|
||||
if (text == styleName)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
QString WizardHandler::styleName(int index) const
|
||||
{
|
||||
auto *field = m_detailsPage->jsonField("ControlsStyle");
|
||||
auto *cbfield = dynamic_cast<ProjectExplorer::ComboBoxField *>(field);
|
||||
QTC_ASSERT(cbfield, return "");
|
||||
|
||||
QStandardItemModel *model = cbfield->model();
|
||||
if (index < 0 || index >= model->rowCount())
|
||||
return {};
|
||||
|
||||
QString text = model->item(index)->text();
|
||||
return text;
|
||||
}
|
||||
|
||||
void WizardHandler::setUseVirtualKeyboard(bool value)
|
||||
{
|
||||
auto *field = m_detailsPage->jsonField("UseVirtualKeyboard");
|
||||
|
@@ -47,15 +47,23 @@ class WizardHandler: public QObject
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
void reset(const PresetItem &presetInfo, int presetSelection);
|
||||
void reset(const std::shared_ptr<PresetItem> &presetInfo, int presetSelection);
|
||||
void setScreenSizeIndex(int index);
|
||||
int screenSizeIndex(const QString &sizeName) const;
|
||||
QString screenSizeName(int index) const;
|
||||
int screenSizeIndex() const;
|
||||
int targetQtVersionIndex() const;
|
||||
int targetQtVersionIndex(const QString &qtVersionName) const;
|
||||
void setTargetQtVersionIndex(int index);
|
||||
bool haveTargetQtVersion() const;
|
||||
QString targetQtVersionName(int index) const;
|
||||
|
||||
void setStyleIndex(int index);
|
||||
int styleIndex() const;
|
||||
int styleIndex(const QString &styleName) const;
|
||||
QString styleName(int index) const;
|
||||
bool haveStyleModel() const;
|
||||
|
||||
void destroyWizard();
|
||||
|
||||
void setUseVirtualKeyboard(bool value);
|
||||
@@ -66,7 +74,7 @@ public:
|
||||
|
||||
void run(const std::function<void (QWizardPage *)> &processPage);
|
||||
|
||||
PresetItem preset() const { return m_preset; }
|
||||
std::shared_ptr<PresetItem> preset() const { return m_preset; }
|
||||
|
||||
signals:
|
||||
void deletingWizard();
|
||||
@@ -93,7 +101,7 @@ private:
|
||||
|
||||
int m_selectedPreset = -1;
|
||||
|
||||
PresetItem m_preset;
|
||||
std::shared_ptr<PresetItem> m_preset;
|
||||
Utils::FilePath m_projectLocation;
|
||||
};
|
||||
|
||||
|
@@ -18,12 +18,14 @@ add_qtc_test(tst_qml_wizard
|
||||
wizardfactories-test.cpp
|
||||
stylemodel-test.cpp
|
||||
recentpresets-test.cpp
|
||||
userpresets-test.cpp
|
||||
presetmodel-test.cpp
|
||||
test-utilities.h
|
||||
test-main.cpp
|
||||
"${StudioWelcomeDir}/wizardfactories.cpp"
|
||||
"${StudioWelcomeDir}/stylemodel.cpp"
|
||||
"${StudioWelcomeDir}/recentpresets.cpp"
|
||||
"${StudioWelcomeDir}/userpresets.cpp"
|
||||
"${StudioWelcomeDir}/presetmodel.cpp"
|
||||
)
|
||||
|
||||
|
@@ -33,10 +33,18 @@ using ::testing::ElementsAreArray;
|
||||
using ::testing::PrintToString;
|
||||
|
||||
namespace StudioWelcome {
|
||||
|
||||
void PrintTo(const UserPresetItem &item, std::ostream *os);
|
||||
|
||||
void PrintTo(const PresetItem &item, std::ostream *os)
|
||||
{
|
||||
if (typeid(item) == typeid(UserPresetItem)) {
|
||||
PrintTo((UserPresetItem &) item, os);
|
||||
return;
|
||||
}
|
||||
|
||||
*os << "{categId: " << item.categoryId << ", "
|
||||
<< "name: " << item.name;
|
||||
<< "name: " << item.wizardName;
|
||||
|
||||
if (!item.screenSizeName.isEmpty())
|
||||
*os << ", size: " << item.screenSizeName;
|
||||
@@ -44,6 +52,26 @@ void PrintTo(const PresetItem &item, std::ostream *os)
|
||||
*os << "}";
|
||||
}
|
||||
|
||||
void PrintTo(const UserPresetItem &item, std::ostream *os)
|
||||
{
|
||||
*os << "{categId: " << item.categoryId << ", "
|
||||
<< "name: " << item.wizardName << ", "
|
||||
<< "user name: " << item.userName;
|
||||
|
||||
if (!item.screenSizeName.isEmpty())
|
||||
*os << ", size: " << item.screenSizeName;
|
||||
|
||||
*os << "}";
|
||||
}
|
||||
|
||||
void PrintTo(const std::shared_ptr<PresetItem> &p, std::ostream *os)
|
||||
{
|
||||
if (p)
|
||||
PrintTo(*p, os);
|
||||
else
|
||||
*os << "{null}";
|
||||
}
|
||||
|
||||
} // namespace StudioWelcome
|
||||
|
||||
namespace {
|
||||
@@ -51,20 +79,47 @@ std::pair<QString, WizardCategory> aCategory(const QString &categId,
|
||||
const QString &categName,
|
||||
const std::vector<QString> &names)
|
||||
{
|
||||
std::vector<PresetItem> items = Utils::transform(names, [&categId](const QString &name) {
|
||||
return PresetItem{name, categId};
|
||||
});
|
||||
std::vector<std::shared_ptr<PresetItem>> items
|
||||
= Utils::transform(names, [&categId](const QString &name) {
|
||||
std::shared_ptr<PresetItem> item{new PresetItem};
|
||||
item->wizardName = name;
|
||||
item->categoryId = categId;
|
||||
|
||||
return item;
|
||||
});
|
||||
return std::make_pair(categId, WizardCategory{categId, categName, items});
|
||||
}
|
||||
|
||||
UserPresetData aUserPreset(const QString &categId, const QString &wizardName, const QString &userName)
|
||||
{
|
||||
UserPresetData preset;
|
||||
preset.categoryId = categId;
|
||||
preset.wizardName = wizardName;
|
||||
preset.name = userName;
|
||||
|
||||
return preset;
|
||||
}
|
||||
|
||||
MATCHER_P2(PresetIs, category, name, PrintToString(PresetItem{name, category}))
|
||||
{
|
||||
return arg.categoryId == category && arg.name == name;
|
||||
return arg->categoryId == category && arg->wizardName == name;
|
||||
}
|
||||
|
||||
MATCHER_P3(UserPresetIs,
|
||||
category,
|
||||
wizardName,
|
||||
userName,
|
||||
PrintToString(UserPresetItem{wizardName, userName, category}))
|
||||
{
|
||||
auto userPreset = dynamic_cast<UserPresetItem *>(arg.get());
|
||||
|
||||
return userPreset->categoryId == category && userPreset->wizardName == wizardName
|
||||
&& userPreset->userName == userName;
|
||||
}
|
||||
|
||||
MATCHER_P3(PresetIs, category, name, size, PrintToString(PresetItem{name, category, size}))
|
||||
{
|
||||
return arg.categoryId == category && arg.name == name && size == arg.screenSizeName;
|
||||
return arg->categoryId == category && arg->wizardName == name && size == arg->screenSizeName;
|
||||
}
|
||||
|
||||
} // namespace
|
||||
@@ -88,6 +143,7 @@ TEST(QdsPresetModel, haveSameArraySizeForPresetsAndCategories)
|
||||
aCategory("A.categ", "A", {"item a", "item b"}),
|
||||
aCategory("B.categ", "B", {"item c", "item d"}),
|
||||
},
|
||||
{/*user presets*/},
|
||||
{/*recents*/});
|
||||
|
||||
ASSERT_THAT(data.presets(), SizeIs(2));
|
||||
@@ -105,6 +161,7 @@ TEST(QdsPresetModel, haveWizardPresetsNoRecents)
|
||||
aCategory("A.categ", "A", {"item a", "item b"}),
|
||||
aCategory("B.categ", "B", {"item c", "item d"}),
|
||||
},
|
||||
{/*user presets*/},
|
||||
{/*recents*/});
|
||||
|
||||
// Then
|
||||
@@ -115,11 +172,30 @@ TEST(QdsPresetModel, haveWizardPresetsNoRecents)
|
||||
ElementsAre(PresetIs("B.categ", "item c"), PresetIs("B.categ", "item d")));
|
||||
}
|
||||
|
||||
TEST(QdsPresetModel, whenHaveUserPresetsButNoWizardPresetsReturnEmpty)
|
||||
{
|
||||
// Given
|
||||
PresetData data;
|
||||
|
||||
// When
|
||||
data.setData({/*builtin presets*/},
|
||||
{
|
||||
aUserPreset("A.Mobile", "Scroll", "iPhone5"),
|
||||
aUserPreset("B.Desktop", "Launcher", "MacBook"),
|
||||
},
|
||||
{/*recents*/});
|
||||
|
||||
// Then
|
||||
ASSERT_THAT(data.categories(), IsEmpty());
|
||||
ASSERT_THAT(data.presets(), IsEmpty());
|
||||
}
|
||||
|
||||
TEST(QdsPresetModel, haveRecentsNoWizardPresets)
|
||||
{
|
||||
PresetData data;
|
||||
|
||||
data.setData({/*wizardPresets*/},
|
||||
{/*user presets*/},
|
||||
{
|
||||
{"A.categ", "Desktop", "640 x 480"},
|
||||
{"B.categ", "Mobile", "800 x 600"},
|
||||
@@ -129,7 +205,7 @@ TEST(QdsPresetModel, haveRecentsNoWizardPresets)
|
||||
ASSERT_THAT(data.presets(), IsEmpty());
|
||||
}
|
||||
|
||||
TEST(QdsPresetModel, recentsAddedBeforeWizardPresets)
|
||||
TEST(QdsPresetModel, recentsAddedWithWizardPresets)
|
||||
{
|
||||
// Given
|
||||
PresetData data;
|
||||
@@ -141,6 +217,7 @@ TEST(QdsPresetModel, recentsAddedBeforeWizardPresets)
|
||||
aCategory("A.categ", "A", {"Desktop", "item b"}),
|
||||
aCategory("B.categ", "B", {"item c", "Mobile"}),
|
||||
},
|
||||
{/*user presets*/},
|
||||
/*recents*/
|
||||
{
|
||||
{"A.categ", "Desktop", "800 x 600"},
|
||||
@@ -158,7 +235,98 @@ TEST(QdsPresetModel, recentsAddedBeforeWizardPresets)
|
||||
ElementsAre(PresetIs("B.categ", "item c"), PresetIs("B.categ", "Mobile"))}));
|
||||
}
|
||||
|
||||
TEST(QdsPresetModel, recentsShouldNotSorted)
|
||||
TEST(QdsPresetModel, userPresetsAddedWithWizardPresets)
|
||||
{
|
||||
// Given
|
||||
PresetData data;
|
||||
|
||||
// When
|
||||
data.setData(
|
||||
/*wizard presets*/
|
||||
{
|
||||
aCategory("A.categ", "A", {"Desktop", "item b"}),
|
||||
aCategory("B.categ", "B", {"Mobile"}),
|
||||
},
|
||||
{
|
||||
aUserPreset("A.categ", "Desktop", "Windows10"),
|
||||
},
|
||||
{/*recents*/});
|
||||
|
||||
// Then
|
||||
ASSERT_THAT(data.categories(), ElementsAre("A", "B", "Custom"));
|
||||
ASSERT_THAT(data.presets(),
|
||||
ElementsAre(ElementsAre(PresetIs("A.categ", "Desktop"),
|
||||
PresetIs("A.categ", "item b")),
|
||||
ElementsAre(PresetIs("B.categ", "Mobile")),
|
||||
ElementsAre(UserPresetIs("A.categ", "Desktop", "Windows10"))));
|
||||
}
|
||||
|
||||
TEST(QdsPresetModel, doesNotAddUserPresetsOfNonExistingCategory)
|
||||
{
|
||||
// Given
|
||||
PresetData data;
|
||||
|
||||
// When
|
||||
data.setData(
|
||||
/*wizard presets*/
|
||||
{
|
||||
aCategory("A.categ", "A", {"Desktop"}), // Only category "A.categ" exists
|
||||
},
|
||||
{
|
||||
aUserPreset("Bad.Categ", "Desktop", "Windows8"), // Bad.Categ does not exist
|
||||
},
|
||||
{/*recents*/});
|
||||
|
||||
// Then
|
||||
ASSERT_THAT(data.categories(), ElementsAre("A"));
|
||||
ASSERT_THAT(data.presets(), ElementsAre(ElementsAre(PresetIs("A.categ", "Desktop"))));
|
||||
}
|
||||
|
||||
TEST(QdsPresetModel, doesNotAddUserPresetIfWizardPresetItRefersToDoesNotExist)
|
||||
{
|
||||
// Given
|
||||
PresetData data;
|
||||
|
||||
// When
|
||||
data.setData(
|
||||
/*wizard presets*/
|
||||
{
|
||||
aCategory("A.categ", "A", {"Desktop"}),
|
||||
},
|
||||
{
|
||||
aUserPreset("B.categ", "BadWizard", "Tablet"), // BadWizard referenced does not exist
|
||||
},
|
||||
{/*recents*/});
|
||||
|
||||
// Then
|
||||
ASSERT_THAT(data.categories(), ElementsAre("A"));
|
||||
ASSERT_THAT(data.presets(), ElementsAre(ElementsAre(PresetIs("A.categ", "Desktop"))));
|
||||
}
|
||||
|
||||
TEST(QdsPresetModel, userPresetWithSameNameAsWizardPreset)
|
||||
{
|
||||
// Given
|
||||
PresetData data;
|
||||
|
||||
// When
|
||||
data.setData(
|
||||
/*wizard presets*/
|
||||
{
|
||||
aCategory("A.categ", "A", {"Desktop"}),
|
||||
},
|
||||
{
|
||||
aUserPreset("A.categ", "Desktop", "Desktop"),
|
||||
},
|
||||
{/*recents*/});
|
||||
|
||||
// Then
|
||||
ASSERT_THAT(data.categories(), ElementsAre("A", "Custom"));
|
||||
ASSERT_THAT(data.presets(),
|
||||
ElementsAre(ElementsAre(PresetIs("A.categ", "Desktop")),
|
||||
ElementsAre(UserPresetIs("A.categ", "Desktop", "Desktop"))));
|
||||
}
|
||||
|
||||
TEST(QdsPresetModel, recentsShouldNotBeSorted)
|
||||
{
|
||||
// Given
|
||||
PresetData data;
|
||||
@@ -171,6 +339,7 @@ TEST(QdsPresetModel, recentsShouldNotSorted)
|
||||
aCategory("B.categ", "B", {"item c", "Mobile"}),
|
||||
aCategory("Z.categ", "Z", {"Z.desktop"}),
|
||||
},
|
||||
{/*user presets*/},
|
||||
/*recents*/
|
||||
{
|
||||
{"Z.categ", "Z.desktop", "200 x 300"},
|
||||
@@ -197,6 +366,7 @@ TEST(QdsPresetModel, recentsOfSameWizardProjectButDifferentSizesAreRecognizedAsD
|
||||
aCategory("A.categ", "A", {"Desktop"}),
|
||||
aCategory("B.categ", "B", {"Mobile"}),
|
||||
},
|
||||
{/*user presets*/},
|
||||
/*recents*/
|
||||
{
|
||||
{"B.categ", "Mobile", "400 x 400"},
|
||||
@@ -223,6 +393,7 @@ TEST(QdsPresetModel, outdatedRecentsAreNotShown)
|
||||
aCategory("A.categ", "A", {"Desktop"}),
|
||||
aCategory("B.categ", "B", {"Mobile"}),
|
||||
},
|
||||
{/*user presets*/},
|
||||
/*recents*/
|
||||
{
|
||||
{"B.categ", "NoLongerExists", "400 x 400"},
|
||||
|
@@ -38,6 +38,16 @@ using namespace StudioWelcome;
|
||||
constexpr char GROUP_NAME[] = "RecentPresets";
|
||||
constexpr char ITEMS[] = "Wizards";
|
||||
|
||||
namespace StudioWelcome {
|
||||
void PrintTo(const RecentPresetData &recent, std::ostream *os)
|
||||
{
|
||||
*os << "{categId: " << recent.category << ", name: " << recent.presetName
|
||||
<< ", size: " << recent.sizeName << ", isUser: " << recent.isUserPreset;
|
||||
|
||||
*os << "}";
|
||||
}
|
||||
} // namespace StudioWelcome
|
||||
|
||||
class QdsRecentPresets : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
@@ -73,7 +83,7 @@ TEST_F(QdsRecentPresets, readFromEmptyStore)
|
||||
{
|
||||
RecentPresetsStore store{&settings};
|
||||
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents, IsEmpty());
|
||||
}
|
||||
@@ -82,7 +92,7 @@ TEST_F(QdsRecentPresets, readEmptyRecentPresets)
|
||||
{
|
||||
RecentPresetsStore store = aStoreWithOne("");
|
||||
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents, IsEmpty());
|
||||
}
|
||||
@@ -91,25 +101,34 @@ TEST_F(QdsRecentPresets, readOneRecentPresetAsList)
|
||||
{
|
||||
RecentPresetsStore store = aStoreWithRecents({"category/preset:640 x 480"});
|
||||
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents, ElementsAre(RecentPreset("category", "preset", "640 x 480")));
|
||||
ASSERT_THAT(recents, ElementsAre(RecentPresetData("category", "preset", "640 x 480")));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, readOneRecentPresetAsString)
|
||||
{
|
||||
RecentPresetsStore store = aStoreWithOne("category/preset:200 x 300");
|
||||
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents, ElementsAre(RecentPreset("category", "preset", "200 x 300")));
|
||||
ASSERT_THAT(recents, ElementsAre(RecentPresetData("category", "preset", "200 x 300")));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, readOneRecentUserPresetAsString)
|
||||
{
|
||||
RecentPresetsStore store = aStoreWithOne("category/[U]preset:200 x 300");
|
||||
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents, ElementsAre(RecentPresetData("category", "preset", "200 x 300", true)));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, readBadRecentPresetAsString)
|
||||
{
|
||||
RecentPresetsStore store = aStoreWithOne("no_category_only_preset");
|
||||
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents, IsEmpty());
|
||||
}
|
||||
@@ -118,24 +137,32 @@ TEST_F(QdsRecentPresets, readBadRecentPresetAsInt)
|
||||
{
|
||||
RecentPresetsStore store = aStoreWithOne(32);
|
||||
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents, IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, readBadRecentPresetsInList)
|
||||
{
|
||||
RecentPresetsStore store = aStoreWithRecents({"bad1", // no category, no size
|
||||
"categ/name:800 x 600", // good
|
||||
"categ/bad2", //no size
|
||||
"categ/bad3:", //no size
|
||||
"categ 1/bad4:200 x 300", // category has space
|
||||
"categ/bad5: 400 x 300", // size starts with space
|
||||
"categ/bad6:400"}); // bad size
|
||||
RecentPresetsStore store = aStoreWithRecents({
|
||||
"bad1", // no category, no size
|
||||
"categ/name:800 x 600", // good
|
||||
"categ/bad2", //no size
|
||||
"categ/bad3:", //no size
|
||||
"categ 1/bad4:200 x 300", // category has space
|
||||
"categ/bad5: 400 x 300", // size starts with space
|
||||
"categ/bad6:400", // bad size
|
||||
"categ/[U]user:300 x 200", // good
|
||||
"categ/[u]user2:300 x 200", // small cap "U"
|
||||
"categ/[x]user3:300 x 200", // must be letter "U"
|
||||
"categ/[U] user4:300 x 200", // space
|
||||
});
|
||||
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents, ElementsAre(RecentPreset("categ", "name", "800 x 600")));
|
||||
ASSERT_THAT(recents,
|
||||
ElementsAre(RecentPresetData("categ", "name", "800 x 600", false),
|
||||
RecentPresetData("categ", "user", "300 x 200", true)));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, readTwoRecentPresets)
|
||||
@@ -143,11 +170,23 @@ TEST_F(QdsRecentPresets, readTwoRecentPresets)
|
||||
RecentPresetsStore store = aStoreWithRecents(
|
||||
{"category_1/preset 1:640 x 480", "category_2/preset 2:320 x 200"});
|
||||
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents,
|
||||
ElementsAre(RecentPreset("category_1", "preset 1", "640 x 480"),
|
||||
RecentPreset("category_2", "preset 2", "320 x 200")));
|
||||
ElementsAre(RecentPresetData("category_1", "preset 1", "640 x 480"),
|
||||
RecentPresetData("category_2", "preset 2", "320 x 200")));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, readRecentsToDifferentKindsOfPresets)
|
||||
{
|
||||
RecentPresetsStore store = aStoreWithRecents(
|
||||
{"category_1/preset 1:640 x 480", "category_2/[U]preset 2:320 x 200"});
|
||||
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents,
|
||||
ElementsAre(RecentPresetData("category_1", "preset 1", "640 x 480", false),
|
||||
RecentPresetData("category_2", "preset 2", "320 x 200", true)));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, addFirstRecentPreset)
|
||||
@@ -155,19 +194,44 @@ TEST_F(QdsRecentPresets, addFirstRecentPreset)
|
||||
RecentPresetsStore store{&settings};
|
||||
|
||||
store.add("A.Category", "Normal Application", "400 x 600");
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents, ElementsAre(RecentPreset("A.Category", "Normal Application", "400 x 600")));
|
||||
ASSERT_THAT(recents, ElementsAre(RecentPresetData("A.Category", "Normal Application", "400 x 600")));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, addFirstRecentUserPreset)
|
||||
{
|
||||
RecentPresetsStore store{&settings};
|
||||
|
||||
store.add("A.Category", "Normal Application", "400 x 600", /*user preset*/ true);
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents,
|
||||
ElementsAre(RecentPresetData("A.Category", "Normal Application", "400 x 600", true)));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, addExistingFirstRecentPreset)
|
||||
{
|
||||
RecentPresetsStore store = aStoreWithRecents({"category/preset"});
|
||||
RecentPresetsStore store = aStoreWithRecents({"category/preset:200 x 300"});
|
||||
ASSERT_THAT(store.fetchAll(), ElementsAre(RecentPresetData("category", "preset", "200 x 300")));
|
||||
|
||||
store.add("category", "preset", "200 x 300");
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents, ElementsAre(RecentPreset("category", "preset", "200 x 300")));
|
||||
ASSERT_THAT(recents, ElementsAre(RecentPresetData("category", "preset", "200 x 300")));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, addRecentUserPresetWithSameNameAsExistingRecentNormalPreset)
|
||||
{
|
||||
RecentPresetsStore store = aStoreWithRecents({"category/preset:200 x 300"});
|
||||
ASSERT_THAT(store.fetchAll(), ElementsAre(RecentPresetData("category", "preset", "200 x 300")));
|
||||
|
||||
store.add("category", "preset", "200 x 300", /*user preset*/ true);
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents,
|
||||
ElementsAre(RecentPresetData("category", "preset", "200 x 300", true),
|
||||
RecentPresetData("category", "preset", "200 x 300", false)));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, addSecondRecentPreset)
|
||||
@@ -175,11 +239,11 @@ TEST_F(QdsRecentPresets, addSecondRecentPreset)
|
||||
RecentPresetsStore store = aStoreWithRecents({"A.Category/Preset 1:800 x 600"});
|
||||
|
||||
store.add("A.Category", "Preset 2", "640 x 480");
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents,
|
||||
ElementsAre(RecentPreset("A.Category", "Preset 2", "640 x 480"),
|
||||
RecentPreset("A.Category", "Preset 1", "800 x 600")));
|
||||
ElementsAre(RecentPresetData("A.Category", "Preset 2", "640 x 480"),
|
||||
RecentPresetData("A.Category", "Preset 1", "800 x 600")));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, addSecondRecentPresetSameKindButDifferentSize)
|
||||
@@ -187,11 +251,11 @@ TEST_F(QdsRecentPresets, addSecondRecentPresetSameKindButDifferentSize)
|
||||
RecentPresetsStore store = aStoreWithRecents({"A.Category/Preset:800 x 600"});
|
||||
|
||||
store.add("A.Category", "Preset", "640 x 480");
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents,
|
||||
ElementsAre(RecentPreset("A.Category", "Preset", "640 x 480"),
|
||||
RecentPreset("A.Category", "Preset", "800 x 600")));
|
||||
ElementsAre(RecentPresetData("A.Category", "Preset", "640 x 480"),
|
||||
RecentPresetData("A.Category", "Preset", "800 x 600")));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, fetchesRecentPresetsInTheReverseOrderTheyWereAdded)
|
||||
@@ -201,12 +265,12 @@ TEST_F(QdsRecentPresets, fetchesRecentPresetsInTheReverseOrderTheyWereAdded)
|
||||
store.add("A.Category", "Preset 1", "640 x 480");
|
||||
store.add("A.Category", "Preset 2", "640 x 480");
|
||||
store.add("A.Category", "Preset 3", "800 x 600");
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents,
|
||||
ElementsAre(RecentPreset("A.Category", "Preset 3", "800 x 600"),
|
||||
RecentPreset("A.Category", "Preset 2", "640 x 480"),
|
||||
RecentPreset("A.Category", "Preset 1", "640 x 480")));
|
||||
ElementsAre(RecentPresetData("A.Category", "Preset 3", "800 x 600"),
|
||||
RecentPresetData("A.Category", "Preset 2", "640 x 480"),
|
||||
RecentPresetData("A.Category", "Preset 1", "640 x 480")));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, addingAnExistingRecentPresetMakesItTheFirst)
|
||||
@@ -216,12 +280,12 @@ TEST_F(QdsRecentPresets, addingAnExistingRecentPresetMakesItTheFirst)
|
||||
"A.Category/Preset 3:640 x 480"});
|
||||
|
||||
store.add("A.Category", "Preset 3", "640 x 480");
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents,
|
||||
ElementsAre(RecentPreset("A.Category", "Preset 3", "640 x 480"),
|
||||
RecentPreset("A.Category", "Preset 1", "200 x 300"),
|
||||
RecentPreset("A.Category", "Preset 2", "200 x 300")));
|
||||
ElementsAre(RecentPresetData("A.Category", "Preset 3", "640 x 480"),
|
||||
RecentPresetData("A.Category", "Preset 1", "200 x 300"),
|
||||
RecentPresetData("A.Category", "Preset 2", "200 x 300")));
|
||||
}
|
||||
|
||||
TEST_F(QdsRecentPresets, addingTooManyRecentPresetsRemovesTheOldestOne)
|
||||
@@ -231,9 +295,9 @@ TEST_F(QdsRecentPresets, addingTooManyRecentPresetsRemovesTheOldestOne)
|
||||
store.setMaximum(2);
|
||||
|
||||
store.add("A.Category", "Preset 3", "200 x 300");
|
||||
std::vector<RecentPreset> recents = store.fetchAll();
|
||||
std::vector<RecentPresetData> recents = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(recents,
|
||||
ElementsAre(RecentPreset("A.Category", "Preset 3", "200 x 300"),
|
||||
RecentPreset("A.Category", "Preset 2", "200 x 300")));
|
||||
ElementsAre(RecentPresetData("A.Category", "Preset 3", "200 x 300"),
|
||||
RecentPresetData("A.Category", "Preset 2", "200 x 300")));
|
||||
}
|
||||
|
308
tests/auto/qml/qmldesigner/wizard/userpresets-test.cpp
Normal file
@@ -0,0 +1,308 @@
|
||||
/****************************************************************************
|
||||
**
|
||||
** Copyright (C) 2022 The Qt Company Ltd.
|
||||
** Contact: https://www.qt.io/licensing/
|
||||
**
|
||||
** This file is part of Qt Creator.
|
||||
**
|
||||
** Commercial License Usage
|
||||
** Licensees holding valid commercial Qt licenses may use this file in
|
||||
** accordance with the commercial license agreement provided with the
|
||||
** Software or, alternatively, in accordance with the terms contained in
|
||||
** a written agreement between you and The Qt Company. For licensing terms
|
||||
** and conditions see https://www.qt.io/terms-conditions. For further
|
||||
** information use the contact form at https://www.qt.io/contact-us.
|
||||
**
|
||||
** GNU General Public License Usage
|
||||
** Alternatively, this file may be used under the terms of the GNU
|
||||
** General Public License version 3 as published by the Free Software
|
||||
** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
|
||||
** included in the packaging of this file. Please review the following
|
||||
** information to ensure the GNU General Public License requirements will
|
||||
** be met: https://www.gnu.org/licenses/gpl-3.0.html.
|
||||
**
|
||||
****************************************************************************/
|
||||
|
||||
#include "test-utilities.h"
|
||||
#include "userpresets.h"
|
||||
|
||||
#include <utils/filepath.h>
|
||||
#include <utils/temporarydirectory.h>
|
||||
|
||||
namespace StudioWelcome {
|
||||
|
||||
void PrintTo(const UserPresetData &preset, std::ostream *os)
|
||||
{
|
||||
*os << "UserPresetData{category = " << preset.categoryId;
|
||||
*os << "; wizardName = " << preset.wizardName;
|
||||
*os << "; name = " << preset.name;
|
||||
*os << "; screenSize = " << preset.screenSize;
|
||||
*os << "; keyboard = " << preset.useQtVirtualKeyboard;
|
||||
*os << "; qt = " << preset.qtVersion;
|
||||
*os << "; style = " << preset.styleName;
|
||||
*os << "}";
|
||||
}
|
||||
|
||||
void PrintTo(const std::vector<UserPresetData> &presets, std::ostream *os)
|
||||
{
|
||||
if (presets.size() == 0) {
|
||||
*os << "{}" << std::endl;
|
||||
} else {
|
||||
*os << "{" << std::endl;
|
||||
for (size_t i = 0; i < presets.size(); ++i) {
|
||||
*os << "#" << i << ": ";
|
||||
PrintTo(presets[i], os);
|
||||
*os << std::endl;
|
||||
}
|
||||
*os << "}" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace StudioWelcome
|
||||
|
||||
using namespace StudioWelcome;
|
||||
|
||||
constexpr char ARRAY_NAME[] = "UserPresets";
|
||||
|
||||
class QdsUserPresets : public ::testing::Test
|
||||
{
|
||||
protected:
|
||||
void SetUp()
|
||||
{
|
||||
settings = std::make_unique<QSettings>(tempDir.filePath("test").toString(),
|
||||
QSettings::IniFormat);
|
||||
}
|
||||
|
||||
UserPresetsStore anEmptyStore() { return UserPresetsStore{std::move(settings)}; }
|
||||
|
||||
UserPresetsStore aStoreWithZeroItems()
|
||||
{
|
||||
settings->beginWriteArray(ARRAY_NAME, 0);
|
||||
settings->endArray();
|
||||
|
||||
return UserPresetsStore{std::move(settings)};
|
||||
}
|
||||
|
||||
UserPresetsStore aStoreWithOne(const UserPresetData &preset)
|
||||
{
|
||||
settings->beginWriteArray(ARRAY_NAME, 1);
|
||||
settings->setArrayIndex(0);
|
||||
|
||||
settings->setValue("categoryId", preset.categoryId);
|
||||
settings->setValue("wizardName", preset.wizardName);
|
||||
settings->setValue("name", preset.name);
|
||||
settings->setValue("screenSize", preset.screenSize);
|
||||
settings->setValue("useQtVirtualKeyboard", preset.useQtVirtualKeyboard);
|
||||
settings->setValue("qtVersion", preset.qtVersion);
|
||||
settings->setValue("styleName", preset.styleName);
|
||||
|
||||
settings->endArray();
|
||||
|
||||
return UserPresetsStore{std::move(settings)};
|
||||
}
|
||||
|
||||
UserPresetsStore aStoreWithPresets(const std::vector<UserPresetData> &presets)
|
||||
{
|
||||
settings->beginWriteArray(ARRAY_NAME, presets.size());
|
||||
|
||||
for (size_t i = 0; i < presets.size(); ++i) {
|
||||
settings->setArrayIndex(i);
|
||||
const auto &preset = presets[i];
|
||||
|
||||
settings->setValue("categoryId", preset.categoryId);
|
||||
settings->setValue("wizardName", preset.wizardName);
|
||||
settings->setValue("name", preset.name);
|
||||
settings->setValue("screenSize", preset.screenSize);
|
||||
settings->setValue("useQtVirtualKeyboard", preset.useQtVirtualKeyboard);
|
||||
settings->setValue("qtVersion", preset.qtVersion);
|
||||
settings->setValue("styleName", preset.styleName);
|
||||
}
|
||||
settings->endArray();
|
||||
|
||||
return UserPresetsStore{std::move(settings)};
|
||||
}
|
||||
|
||||
Utils::TemporaryDirectory tempDir{"userpresets-XXXXXX"};
|
||||
std::unique_ptr<QSettings> settings;
|
||||
|
||||
private:
|
||||
QString settingsPath;
|
||||
};
|
||||
|
||||
/******************* TESTS *******************/
|
||||
|
||||
TEST_F(QdsUserPresets, readEmptyUserPresets)
|
||||
{
|
||||
auto store = anEmptyStore();
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(presets, IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, readEmptyArrayOfUserPresets)
|
||||
{
|
||||
auto store = aStoreWithZeroItems();
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(presets, IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, readOneUserPreset)
|
||||
{
|
||||
UserPresetData preset{"A.categ", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"};
|
||||
auto store = aStoreWithOne(preset);
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(presets, ElementsAreArray({preset}));
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, readOneIncompleteUserPreset)
|
||||
{
|
||||
// A preset entry that has the required entries, but not the others.
|
||||
UserPresetData preset{"A.categ", "3D App", "iPhone7", "", false, "", ""};
|
||||
auto store = aStoreWithOne(preset);
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(presets, ElementsAreArray({preset}));
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, doesNotReadPresetsThatLackRequiredEntries)
|
||||
{
|
||||
// Required entries are: Category id, wizard name, preset name.
|
||||
auto presetsInStore = std::vector<UserPresetData>{
|
||||
UserPresetData{"", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"},
|
||||
UserPresetData{"A.categ", "", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"},
|
||||
UserPresetData{"A.categ", "3D App", "", "400 x 20", true, "Qt 5", "Material Dark"},
|
||||
};
|
||||
auto store = aStoreWithPresets(presetsInStore);
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(presets, IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, readSeveralUserPresets)
|
||||
{
|
||||
auto presetsInStore = std::vector<UserPresetData>{
|
||||
UserPresetData{"A.categ", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"},
|
||||
UserPresetData{"B.categ", "2D App", "iPhone6", "200 x 50", false, "Qt 6", "Fusion"},
|
||||
UserPresetData{"C.categ", "Empty", "Some Other", "60 x 30", true, "Qt 7", "Material Light"},
|
||||
};
|
||||
auto store = aStoreWithPresets(presetsInStore);
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
|
||||
ASSERT_THAT(presets, ElementsAreArray(presetsInStore));
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, cannotSaveInvalidPreset)
|
||||
{
|
||||
// an invalid preset is a preset that lacks required fields.
|
||||
UserPresetData invalidPreset{"", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"};
|
||||
auto store = anEmptyStore();
|
||||
|
||||
bool saved = store.save(invalidPreset);
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
ASSERT_THAT(presets, IsEmpty());
|
||||
ASSERT_FALSE(saved);
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, savePresetInEmptyStore)
|
||||
{
|
||||
UserPresetData preset{"B.categ", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"};
|
||||
auto store = anEmptyStore();
|
||||
|
||||
store.save(preset);
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
ASSERT_THAT(presets, ElementsAre(preset));
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, saveIncompletePreset)
|
||||
{
|
||||
UserPresetData preset{"C.categ", "2D App", "Android", "", false, "", ""};
|
||||
auto store = anEmptyStore();
|
||||
|
||||
store.save(preset);
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
ASSERT_THAT(presets, ElementsAre(preset));
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, cannotSavePresetWithSameName)
|
||||
{
|
||||
UserPresetData existing{"B.categ", "3D App", "Same Name", "400 x 20", true, "Qt 5", "Material Dark"};
|
||||
auto store = aStoreWithOne(existing);
|
||||
|
||||
UserPresetData newPreset{"C.categ", "Empty", "Same Name", "100 x 30", false, "Qt 6", "Fusion"};
|
||||
bool saved = store.save(newPreset);
|
||||
|
||||
ASSERT_FALSE(saved);
|
||||
ASSERT_THAT(store.fetchAll(), ElementsAreArray({existing}));
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, saveNewPreset)
|
||||
{
|
||||
UserPresetData existing{"A.categ", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"};
|
||||
auto store = aStoreWithOne(existing);
|
||||
|
||||
UserPresetData newPreset{"A.categ", "Empty", "Huawei", "100 x 30", true, "Qt 6", "Fusion"};
|
||||
store.save(newPreset);
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
ASSERT_THAT(presets, ElementsAre(existing, newPreset));
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, removeUserPresetFromEmptyStore)
|
||||
{
|
||||
UserPresetData preset{"C.categ", "2D App", "Android", "", false, "", ""};
|
||||
auto store = anEmptyStore();
|
||||
|
||||
store.remove("A.categ", "User preset name");
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
ASSERT_THAT(presets, IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, removeExistingUserPreset)
|
||||
{
|
||||
UserPresetData existing{"A.categ", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"};
|
||||
auto store = aStoreWithOne(existing);
|
||||
|
||||
store.remove("A.categ", "iPhone7");
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
ASSERT_THAT(presets, IsEmpty());
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, removeNonExistingUserPreset)
|
||||
{
|
||||
UserPresetData existing{"A.categ", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"};
|
||||
auto store = aStoreWithOne(existing);
|
||||
|
||||
store.remove("A.categ", "Android");
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
ASSERT_THAT(presets, ElementsAre(existing));
|
||||
}
|
||||
|
||||
TEST_F(QdsUserPresets, removeExistingUserPresetInStoreWithManyPresets)
|
||||
{
|
||||
auto presetsInStore = std::vector<UserPresetData>{
|
||||
UserPresetData{"A.categ", "3D App", "iPhone7", "400 x 20", true, "Qt 5", "Material Dark"},
|
||||
UserPresetData{"B.categ", "2D App", "iPhone6", "200 x 50", false, "Qt 6", "Fusion"},
|
||||
UserPresetData{"C.categ", "Empty", "Some Other", "60 x 30", true, "Qt 7", "Material Light"},
|
||||
};
|
||||
auto store = aStoreWithPresets(presetsInStore);
|
||||
|
||||
store.remove("B.categ", "iPhone6");
|
||||
|
||||
auto presets = store.fetchAll();
|
||||
ASSERT_THAT(presets, ElementsAre(presetsInStore[0], presetsInStore[2]));
|
||||
}
|
||||
|
@@ -151,7 +151,7 @@ private:
|
||||
|
||||
QStringList presetNames(const WizardCategory &cat)
|
||||
{
|
||||
QStringList result = Utils::transform<QStringList>(cat.items, &PresetItem::name);
|
||||
QStringList result = Utils::transform<QStringList>(cat.items, &PresetItem::wizardName);
|
||||
return result;
|
||||
}
|
||||
|
||||
@@ -319,9 +319,9 @@ TEST_F(QdsWizardFactories, createsPresetItemAndCategoryCorrectlyFromWizardFactor
|
||||
ASSERT_EQ("myDisplayCategory", category.name);
|
||||
|
||||
auto presetItem = presets["myCategoryId"].items[0];
|
||||
ASSERT_EQ("myName", presetItem.name);
|
||||
ASSERT_EQ("myDescription", presetItem.description);
|
||||
ASSERT_EQ("qrc:/my/qml/path", presetItem.qmlPath.toString());
|
||||
ASSERT_EQ("\uABCD", presetItem.fontIconCode);
|
||||
ASSERT_EQ("myName", presetItem->wizardName);
|
||||
ASSERT_EQ("myDescription", presetItem->description);
|
||||
ASSERT_EQ("qrc:/my/qml/path", presetItem->qmlPath.toString());
|
||||
ASSERT_EQ("\uABCD", presetItem->fontIconCode);
|
||||
}
|
||||
|
||||
|