1
0
Fork 0

Add-ons page UI improvements based on feedback

This commit is contained in:
James Turner 2018-04-16 15:04:21 +01:00
parent 510f2de84d
commit 8a390dab2a
7 changed files with 415 additions and 379 deletions

View file

@ -2,412 +2,426 @@ import QtQuick 2.4
import FlightGear.Launcher 1.0
import "."
Flickable {
flickableDirection: Flickable.VerticalFlick
contentHeight: contents.childrenRect.height
Item {
Flickable {
id: flick
height: parent.height
width: parent.width - scrollbar.width
flickableDirection: Flickable.VerticalFlick
contentHeight: contents.childrenRect.height
interactive: false
Component {
id: catalogDelegate
Component {
id: catalogDelegate
Rectangle {
id: delegateRoot
Rectangle {
id: delegateRoot
// don't show the delegate for newly added catalogs, until the
// adding process is complete
visible: !model.isNewlyAdded
// don't show the delegate for newly added catalogs, until the
// adding process is complete
visible: !model.isNewlyAdded
height: catalogTextColumn.childrenRect.height + Style.margin * 2
border.width: 1
border.color: Style.themeColor
width: catalogsColumn.width
height: catalogTextColumn.childrenRect.height + Style.margin * 2
border.width: 1
border.color: Style.themeColor
width: catalogsColumn.width
Column {
id: catalogTextColumn
Column {
id: catalogTextColumn
y: Style.margin
anchors.left: parent.left
anchors.leftMargin: Style.margin
anchors.right: catalogDeleteButton.left
anchors.rightMargin: Style.margin
spacing: Style.margin
y: Style.margin
anchors.left: parent.left
anchors.leftMargin: Style.margin
anchors.right: catalogDeleteButton.left
anchors.rightMargin: Style.margin
spacing: Style.margin
Text {
font.pixelSize: Style.subHeadingFontPixelSize
font.bold: true
width: parent.width
text: model.name
}
Text {
visible: model.status === CatalogListModel.Ok
width: parent.width
text: model.description
wrapMode: Text.WordWrap
}
ClickableText {
visible: model.status !== CatalogListModel.Ok
width: parent.width
wrapMode: Text.WordWrap
text: qsTr("This hangar is currently disabled due to a problem. " +
"Click here to try updating the hangar information from the server. "
+ "(An Internet connection is required for this)");
onClicked: {
_addOns.catalogs.refreshCatalog(model.index)
}
}
Text {
width: parent.width
text: model.url
}
Text {
font.pixelSize: Style.subHeadingFontPixelSize
font.bold: true
width: parent.width
text: model.name
}
Text {
visible: model.status === CatalogListModel.Ok
width: parent.width
text: model.description
wrapMode: Text.WordWrap
}
DeleteButton {
id: catalogDeleteButton
anchors.right: parent.right
anchors.rightMargin: Style.margin
anchors.verticalCenter: parent.verticalCenter
visible: delegateHover.containsMouse
ClickableText {
visible: model.status !== CatalogListModel.Ok
width: parent.width
wrapMode: Text.WordWrap
text: qsTr("This hangar is currently disabled due to a problem. " +
"Click here to try updating the hangar information from the server. "
+ "(An Internet connection is required for this)");
onClicked: {
_addOns.catalogs.refreshCatalog(model.index)
confirmDeleteHangar.visible = true;
}
}
MouseArea {
id: delegateHover
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
}
YesNoPanel {
id: confirmDeleteHangar
visible: false
anchors.fill: parent
yesText: qsTr("Remove")
noText: qsTr("Cancel")
yesIsDestructive: true
promptText: qsTr("Remove this hangar? (Downloaded aircraft will be deleted from your computer)");
onAccepted: {
_addOns.catalogs.removeCatalog(model.index);
}
onRejected: {
confirmDeleteHangar.visible = false
}
}
}
}
Column {
id: contents
width: parent.width - (Style.margin * 2)
x: Style.margin
spacing: Style.margin
//////////////////////////////////////////////////////////////////
// catalogs //////////////////////////////////////////////////////
Item {
id: catalogHeaderItem
width: parent.width
height: catalogHeadingText.height + catalogDescriptionText.height + Style.margin
Text {
id: catalogHeadingText
text: qsTr("Aircraft hangars")
font.pixelSize: Style.headingFontPixelSize
width: parent.width
}
Text {
id: catalogDescriptionText
text: qsTr("Aircraft hangars are managed collections of aircraft, which can be " +
"downloaded, installed and updated inside FlightGear.")
anchors {
top: catalogHeadingText.bottom
topMargin: Style.margin
}
width: parent.width
wrapMode: Text.WordWrap
}
} // of catalogs header item
Rectangle {
width: parent.width
height: catalogsColumn.childrenRect.height + Style.margin * 2
border.width: 1
border.color: Style.frameColor
clip: true
Column {
id: catalogsColumn
width: parent.width - Style.margin * 2
x: Style.margin
y: Style.margin
spacing: Style.margin
Repeater {
id: catalogsRepeater
model: _addOns.catalogs
delegate: catalogDelegate
}
ClickableText {
visible: !_addOns.isOfficialHangarRegistered && !addCatalogPanel.isActive
width: parent.width
text : qsTr("The official FlightGear aircraft hangar is not set up. To add it, click here.");
onClicked: {
_addOns.catalogs.installDefaultCatalog()
}
}
AddCatalogPanel {
id: addCatalogPanel
width: parent.width
}
}
}
//////////////////////////////////////////////////////////////////
Item {
// spacing item
width: parent.width
height: Style.margin * 2
}
Item {
id: aircraftHeaderItem
width: parent.width
height: aircraftHeading.height + aircraftDescriptionText.height + Style.margin
Text {
id: aircraftHeading
text: qsTr("Additional aircraft folders")
font.pixelSize: Style.headingFontPixelSize
anchors {
left: parent.left
right: addAircraftPathButton.left
rightMargin: Style.margin
}
}
Text {
width: parent.width
text: model.url
id: aircraftDescriptionText
text: qsTr("To use aircraft you download yourself, FlightGear needs to " +
"know the folder(s) containing the aircraft data.")
anchors {
top: aircraftHeading.bottom
topMargin: Style.margin
left: parent.left
right: addAircraftPathButton.left
rightMargin: Style.margin
}
wrapMode: Text.WordWrap
}
AddButton {
id: addAircraftPathButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onClicked: {
var newPath =_addOns.addAircraftPath();
if (newPath !== "") {
_addOns.aircraftPaths.push(newPath)
}
}
}
} // of aircraft header item
Rectangle {
width: parent.width
height: aircraftPathsColumn.childrenRect.height + 1
border.width: 1
border.color: Style.frameColor
clip: true
Column {
id: aircraftPathsColumn
width: parent.width - Style.margin * 2
x: Style.margin
Repeater {
id: aircraftPathsRepeater
model: _addOns.aircraftPaths
delegate: PathListDelegate {
width: aircraftPathsColumn.width
deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData);
modelCount: _addOns.aircraftPaths.length
onPerformDelete: {
var modifiedPaths = _addOns.aircraftPaths.slice()
modifiedPaths.splice(model.index, 1);
_addOns.aircraftPaths = modifiedPaths;
}
onPerformMove: {
var modifiedPaths = _addOns.aircraftPaths.slice()
modifiedPaths.splice(model.index, 1);
modifiedPaths.splice(newIndex, 0, modelData)
_addOns.aircraftPaths = modifiedPaths;
}
}
}
Text {
visible: (aircraftPathsRepeater.count == 0)
width: parent.width
text : qsTr("No custom aircraft paths are configured.");
}
}
}
DeleteButton {
id: catalogDeleteButton
anchors.right: parent.right
anchors.rightMargin: Style.margin
anchors.verticalCenter: parent.verticalCenter
visible: delegateHover.containsMouse
//////////////////////////////////////////////////////////////////
onClicked: {
confirmDeleteHangar.visible = true;
Item {
// spacing item
width: parent.width
height: Style.margin * 2
}
Item {
id: sceneryHeaderItem
width: parent.width
height: sceneryHeading.height + sceneryDescriptionText.height + Style.margin
Text {
id: sceneryHeading
text: qsTr("Additional scenery folders")
font.pixelSize: Style.headingFontPixelSize
anchors {
left: parent.left
right: addSceneryPathButton.left
rightMargin: Style.margin
}
}
Text {
id: sceneryDescriptionText
text: qsTr("To use scenery you download yourself, FlightGear needs " +
"to know the folders containing the scenery data. " +
"Adjust the order of the list to control which scenery is used in a region.");
anchors {
top: sceneryHeading.bottom
topMargin: Style.margin
left: parent.left
right: addSceneryPathButton.left
rightMargin: Style.margin
}
wrapMode: Text.WordWrap
}
AddButton {
id: addSceneryPathButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onClicked: {
var newPath =_addOns.addSceneryPath();
if (newPath !== "") {
_addOns.sceneryPaths.push(newPath)
}
}
}
} // of aircraft header item
Rectangle {
width: parent.width
height: sceneryPathsColumn.childrenRect.height + 1
border.width: 1
border.color: Style.frameColor
clip: true
Column {
id: sceneryPathsColumn
width: parent.width - Style.margin * 2
x: Style.margin
Repeater {
id: sceneryPathsRepeater
model: _addOns.sceneryPaths
delegate: PathListDelegate {
width: sceneryPathsColumn.width
deletePromptText: qsTr("Remove the scenery folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData);
modelCount: _addOns.sceneryPaths.length
onPerformDelete: {
var modifiedPaths = _addOns.sceneryPaths.slice()
modifiedPaths.splice(model.index, 1);
_addOns.sceneryPaths = modifiedPaths;
}
onPerformMove: {
}
}
}
Text {
visible: (sceneryPathsRepeater.count == 0)
width: parent.width
text : qsTr("No custom scenery paths are configured.");
}
}
}
MouseArea {
id: delegateHover
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
}
Item {
width: parent.width
height: Math.max(installTarballText.implicitHeight, installTarballButton.height)
Button {
id: installTarballButton
text: "Install add-on scenery"
YesNoPanel {
id: confirmDeleteHangar
visible: false
anchors.fill: parent
onClicked: {
var path = _addOns.installCustomScenery();
if (path !== "") {
// insert into scenery paths if not already present
for (var p in _addOns.sceneryPaths) {
if (p === path)
return; // found, we are are done
}
yesText: qsTr("Remove")
noText: qsTr("Cancel")
yesIsDestructive: true
promptText: qsTr("Remove this hangar? (Downloaded aircraft will be deleted from your computer)");
onAccepted: {
_addOns.catalogs.removeCatalog(model.index);
// not found, add it
_addOns.sceneryPaths.push(path);
}
}
}
onRejected: {
confirmDeleteHangar.visible = false
Text {
id: installTarballText
anchors {
left: installTarballButton.right
right: parent.right
leftMargin: Style.margin
}
wrapMode: Text.WordWrap
text: qsTr("If you have downloaded scenery manually from the official FlightGear website, " +
"you can use this button to extract and install it into a suitable folder. " +
"(Scenery downloaded this way should have a file name such as 'w40n020.tar.gz')"
)
}
}
} // of install-tarbal item
} // of column
}
}
Column {
id: contents
width: parent.width - (Style.margin * 2)
x: Style.margin
spacing: Style.margin
//////////////////////////////////////////////////////////////////
// catalogs //////////////////////////////////////////////////////
Item {
id: catalogHeaderItem
width: parent.width
height: catalogHeadingText.height + catalogDescriptionText.height + Style.margin
Text {
id: catalogHeadingText
text: qsTr("Aircraft hangars")
font.pixelSize: Style.headingFontPixelSize
width: parent.width
}
Text {
id: catalogDescriptionText
text: qsTr("Aircraft hangars are managed collections of aircraft, which can be " +
"downloaded, installed and updated inside FlightGear.")
anchors {
top: catalogHeadingText.bottom
topMargin: Style.margin
}
width: parent.width
wrapMode: Text.WordWrap
}
} // of catalogs header item
Rectangle {
width: parent.width
height: catalogsColumn.childrenRect.height + Style.margin * 2
border.width: 1
border.color: Style.frameColor
clip: true
Column {
id: catalogsColumn
width: parent.width - Style.margin * 2
x: Style.margin
y: Style.margin
spacing: Style.margin
Repeater {
id: catalogsRepeater
model: _addOns.catalogs
delegate: catalogDelegate
}
ClickableText {
visible: !_addOns.isOfficialHangarRegistered && !addCatalogPanel.isActive
width: parent.width
text : qsTr("The official FlightGear aircraft hangar is not set up. To add it, click here.");
onClicked: {
_addOns.catalogs.installDefaultCatalog()
}
}
AddCatalogPanel {
id: addCatalogPanel
width: parent.width
}
}
}
//////////////////////////////////////////////////////////////////
Item {
// spacing item
width: parent.width
height: Style.margin * 2
}
Item {
id: aircraftHeaderItem
width: parent.width
height: aircraftHeading.height + aircraftDescriptionText.height + Style.margin
Text {
id: aircraftHeading
text: qsTr("Additional aircraft folders")
font.pixelSize: Style.headingFontPixelSize
anchors {
left: parent.left
right: addAircraftPathButton.left
rightMargin: Style.margin
}
}
Text {
id: aircraftDescriptionText
text: qsTr("To use aircraft you download yourself, FlightGear needs to " +
"know the folder(s) containing the aircraft data.")
anchors {
top: aircraftHeading.bottom
topMargin: Style.margin
left: parent.left
right: addAircraftPathButton.left
rightMargin: Style.margin
}
wrapMode: Text.WordWrap
}
AddButton {
id: addAircraftPathButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onClicked: {
var newPath =_addOns.addAircraftPath();
if (newPath != "") {
_addOns.aircraftPaths.push(newPath)
}
}
}
} // of aircraft header item
Rectangle {
width: parent.width
height: aircraftPathsColumn.childrenRect.height + 1
border.width: 1
border.color: Style.frameColor
clip: true
Column {
id: aircraftPathsColumn
width: parent.width - Style.margin * 2
x: Style.margin
Repeater {
id: aircraftPathsRepeater
model: _addOns.aircraftPaths
delegate: PathListDelegate {
width: aircraftPathsColumn.width
deletePromptText: qsTr("Remove the aircraft folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData);
onPerformDelete: {
var modifiedPaths = _addOns.aircraftPaths.slice()
modifiedPaths.splice(model.index, 1);
_addOns.aircraftPaths = modifiedPaths;
}
onPerformMove: {
var modifiedPaths = _addOns.aircraftPaths.slice()
modifiedPaths.splice(model.index, 1);
modifiedPaths.splice(newIndex, 0, modelData)
_addOns.aircraftPaths = modifiedPaths;
}
}
}
Text {
visible: (aircraftPathsRepeater.count == 0)
width: parent.width
text : qsTr("No custom aircraft paths are configured.");
}
}
}
//////////////////////////////////////////////////////////////////
Item {
// spacing item
width: parent.width
height: Style.margin * 2
}
Item {
id: sceneryHeaderItem
width: parent.width
height: sceneryHeading.height + sceneryDescriptionText.height + Style.margin
Text {
id: sceneryHeading
text: qsTr("Additional scenery folders")
font.pixelSize: Style.headingFontPixelSize
anchors {
left: parent.left
right: addSceneryPathButton.left
rightMargin: Style.margin
}
}
Text {
id: sceneryDescriptionText
text: qsTr("To use scenery you download yourself, FlightGear needs " +
"to know the folders containing the scenery data. " +
"Adjust the order of the list to control which scenery is used in a region.");
anchors {
top: sceneryHeading.bottom
topMargin: Style.margin
left: parent.left
right: addSceneryPathButton.left
rightMargin: Style.margin
}
wrapMode: Text.WordWrap
}
AddButton {
id: addSceneryPathButton
anchors.right: parent.right
anchors.verticalCenter: parent.verticalCenter
onClicked: {
var newPath =_addOns.addSceneryPath();
if (newPath !== "") {
_addOns.sceneryPaths.push(newPath)
}
}
}
} // of aircraft header item
Rectangle {
width: parent.width
height: sceneryPathsColumn.childrenRect.height + 1
border.width: 1
border.color: Style.frameColor
clip: true
Column {
id: sceneryPathsColumn
width: parent.width - Style.margin * 2
x: Style.margin
Repeater {
id: sceneryPathsRepeater
model: _addOns.sceneryPaths
delegate: PathListDelegate {
width: sceneryPathsColumn.width
deletePromptText: qsTr("Remove the scenery folder: '%1' from the list? (The folder contents will not be changed)").arg(modelData);
onPerformDelete: {
var modifiedPaths = _addOns.sceneryPaths.slice()
modifiedPaths.splice(model.index, 1);
_addOns.sceneryPaths = modifiedPaths;
}
onPerformMove: {
}
}
}
Text {
visible: (sceneryPathsRepeater.count == 0)
width: parent.width
text : qsTr("No custom scenery paths are configured.");
}
}
}
Item {
width: parent.width
height: Math.max(installTarballText.implicitHeight, installTarballButton.height)
Button {
id: installTarballButton
text: "Install add-on scenery"
onClicked: {
var path = _addOns.installCustomScenery();
if (path != "") {
// insert into scenery paths if not already present
for (var p in _addOns.sceneryPaths) {
if (p === path)
return; // found, we are are done
}
// not found, add it
_addOns.sceneryPaths.push(path);
}
}
}
Text {
id: installTarballText
anchors {
left: installTarballButton.right
right: parent.right
leftMargin: Style.margin
}
wrapMode: Text.WordWrap
text: qsTr("If you have downloaded scenery manually from the official FlightGear website, " +
"you can use this button to extract and install it into a suitable folder. " +
"(Scenery downloaded this way should have a file name such as 'w40n020.tar.gz')"
)
}
} // of install-tarbal item
} // of column
Scrollbar {
id: scrollbar
anchors.right: parent.right
height: parent.height
flickable: flick
visible: flick.contentHeight > flick.height
}
}

View file

@ -14,7 +14,7 @@ Item {
id: icon
x: 0
y: 0
source: "qrc:///cancel-icon"
source: "qrc:///cancel-icon-small"
}
MouseArea {

View file

@ -1,9 +1,13 @@
import QtQuick 2.4
import "."
Item {
property bool canMoveUp: true
property bool canMoveDown: true
width: height
height: icon.implicitHeight + 1
visible: canMoveDown || canMoveUp
signal moveUp();
signal moveDown();
@ -12,12 +16,14 @@ Item {
id: icon
x: 0
y: 0
source: "qrc:///reorder-list-icon"
source: "qrc:///reorder-list-icon-small"
}
MouseArea {
id: moveUpArea
enabled: parent.canMoveUp
hoverEnabled: enabled
height: parent.height / 2
width: parent.width
onClicked: {
@ -27,6 +33,8 @@ Item {
MouseArea {
id: moveDownArea
enabled: parent.canMoveDown
hoverEnabled: enabled
height: parent.height / 2
width: parent.width
anchors.bottom: parent.bottom
@ -34,4 +42,12 @@ Item {
parent.moveDown();
}
}
Text {
anchors.verticalCenter: parent.verticalCenter
anchors.right: parent.left
anchors.rightMargin: Style.margin
visible: moveUpArea.containsMouse || moveDownArea.containsMouse
text: moveUpArea.containsMouse ? qsTr("Move up") : qsTr("Move down")
}
}

View file

@ -9,6 +9,7 @@ Item {
signal performMove(var newIndex);
property alias deletePromptText: confirmDeletePath.promptText
property int modelCount: 0
height: childrenRect.height
@ -72,7 +73,10 @@ Item {
id: reorderButton
anchors.right: pathDeleteButton.left
anchors.rightMargin: Style.margin
visible: pathDelegateHover.containsMouse
visible: pathDelegateHover.containsMouse && (canMoveDown || canMoveUp)
canMoveUp: model.index > 0
canMoveDown: model.index < (delegateRoot.modelCount - 1)
onMoveUp: {
if (model.index === 0)

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

View file

@ -78,6 +78,8 @@
<file>qml/AddButton.qml</file>
<file alias="cancel-icon">qml/icons8-cancel-50.png</file>
<file alias="reorder-list-icon">qml/icons8-drag-reorder-filled-50.png</file>
<file alias="cancel-icon-small">qml/icons8-cancel-25.png</file>
<file alias="reorder-list-icon-small">qml/icons8-drag-reorder-filled-25.png</file>
<file alias="add-icon">qml/icons8-plus-26.png</file>
<file>qml/DeleteButton.qml</file>
<file>qml/YesNoPanel.qml</file>