1
0
Fork 0

Grid-view for the launcher

This commit is contained in:
James Turner 2018-11-07 09:23:17 +01:00
parent df810f7970
commit 0239d5ef44
13 changed files with 462 additions and 121 deletions

View file

@ -106,6 +106,8 @@ LauncherController::LauncherController(QObject *parent, QWindow* window) :
LocalAircraftCache::instance()->scanDirs();
m_aircraftModel->setPackageRoot(globals->packageRoot());
m_aircraftGridMode = settings.value("aircraftGridMode").toBool();
m_subsystemIdleTimer = new QTimer(this);
m_subsystemIdleTimer->setInterval(10);
connect(m_subsystemIdleTimer, &QTimer::timeout, []()
@ -821,13 +823,24 @@ void LauncherController::saveConfigAs()
m_config->saveConfigToFile(file);
}
void LauncherController::setAircraftGridMode(bool aircraftGridMode)
{
if (m_aircraftGridMode == aircraftGridMode)
return;
QSettings settings;
settings.setValue("aircraftGridMode", aircraftGridMode);
m_aircraftGridMode = aircraftGridMode;
emit aircraftGridModeChanged(m_aircraftGridMode);
}
void LauncherController::setMinWindowSize(QSize sz)
{
if (sz == m_minWindowSize)
return;
if (sz == m_minWindowSize)
return;
m_window->setMinimumSize(sz);
emit minWindowSizeChanged();
m_window->setMinimumSize(sz);
emit minWindowSizeChanged();
}
QUrl LauncherController::flyIconUrl() const

View file

@ -86,6 +86,7 @@ class LauncherController : public QObject
Q_PROPERTY(QUrl flyIconUrl READ flyIconUrl NOTIFY selectedAircraftChanged)
Q_PROPERTY(bool aircraftGridMode READ aircraftGridMode WRITE setAircraftGridMode NOTIFY aircraftGridModeChanged)
public:
explicit LauncherController(QObject *parent, QWindow* win);
@ -187,6 +188,11 @@ public:
QUrl flyIconUrl() const;
bool aircraftGridMode() const
{
return m_aircraftGridMode;
}
signals:
void selectedAircraftChanged(QUrl selectedAircraft);
@ -199,6 +205,8 @@ signals:
void viewCommandLine();
void aircraftGridModeChanged(bool aircraftGridMode);
public slots:
void setSelectedAircraft(QUrl selectedAircraft);
@ -217,6 +225,8 @@ public slots:
void openConfig();
void saveConfigAs();
void setAircraftGridMode(bool aircraftGridMode);
private slots:
void onAircraftInstalledCompleted(QModelIndex index);
@ -271,6 +281,7 @@ private:
bool m_inAppMode = false;
bool m_keepRunningInAppMode = false;
bool m_appModeResult = true;
bool m_aircraftGridMode;
};
#endif // LAUNCHERCONTROLLER_HXX

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 24 24" version="1.1" fill="#1b7ad3" width="24px" height="24px">
<g id="surface1" fill="#1b7ad3">
<path style=" " d="M 4 4 L 4 8 L 8 8 L 8 4 Z M 10 4 L 10 8 L 14 8 L 14 4 Z M 16 4 L 16 8 L 20 8 L 20 4 Z M 4 10 L 4 14 L 8 14 L 8 10 Z M 10 10 L 10 14 L 14 14 L 14 10 Z M 16 10 L 16 14 L 20 14 L 20 10 Z M 4 16 L 4 20 L 8 20 L 8 16 Z M 10 16 L 10 20 L 14 20 L 14 16 Z M 16 16 L 16 20 L 20 20 L 20 16 Z " fill="#1b7ad3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 564 B

View file

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" viewBox="0 0 26 26" version="1.1" fill="#1b7ad3" width="26px" height="26px">
<g id="surface1" fill="#1b7ad3">
<path style=" " d="M 0 4 L 0 6 L 26 6 L 26 4 Z M 0 12 L 0 14 L 26 14 L 26 12 Z M 0 20 L 0 22 L 26 22 L 26 20 Z " fill="#1b7ad3"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 374 B

View file

@ -0,0 +1,96 @@
import QtQuick 2.4
import FlightGear.Launcher 1.0
import "."
Item {
id: root
signal select(var uri);
signal showDetails(var uri)
readonly property bool __isSelected: (_launcher.selectedAircraft === model.uri)
Rectangle {
anchors.centerIn: parent
width: parent.width - 4
height: parent.height - 4
color: "transparent"
border.width: 1
border.color: "#dfdfdf"
}
MouseArea {
id: mouse
anchors.fill: parent
onClicked: {
if (__isSelected) {
root.showDetails(model.uri)
} else {
root.select(model.uri)
}
}
}
Column {
id: contentBox
width: parent.width
y: Style.margin
spacing: 2 //
Item {
id: thumbnailBox
width: 172
height: 128
anchors.horizontalCenter: parent.horizontalCenter
Rectangle {
anchors.centerIn: parent
border.width: 1
border.color: "#7f7f7f"
width: thumbnail.width
height: thumbnail.height
ThumbnailImage {
id: thumbnail
aircraftUri: model.uri
maximumSize.width: 172
maximumSize.height: 128
}
}
Button {
visible: hover.containsMouse && (model.packageStatus === LocalAircraftCache.PackageNotInstalled)
text: qsTr("Install")
onClicked: {
// drill down and also start the install
_launcher.requestInstallUpdate(model.uri)
root.showDetails(model.uri)
}
anchors {
horizontalCenter: parent.horizontalCenter
bottom: parent.bottom
}
}
}
AircraftVariantChoice {
id: titleBox
width: parent.width
popupFontPixelSize: Style.baseFontPixelSize
aircraft: model.uri;
currentIndex: model.activeVariant
onSelected: {
model.activeVariant = index
root.select(model.uri)
}
}
}
MouseArea {
id: hover
anchors.fill: parent
hoverEnabled: true
acceptedButtons: Qt.NoButton
}
} // of root item

View file

@ -0,0 +1,77 @@
import QtQuick 2.0
import FlightGear.Launcher 1.0 as FG
Item {
id: root
property alias model: view.model
property alias header: view.header
signal showDetails(var uri);
function updateSelectionFromLauncher()
{
var row = model.indexForURI(_launcher.selectedAircraft);
if (row >= 0) {
view.currentIndex = row;
} else {
// clear selection in view, so we don't show something
// erroneous such as the previous value
view.currentIndex = -1;
}
}
onModelChanged: updateSelectionFromLauncher();
Component {
id: highlight
Rectangle {
gradient: Gradient {
GradientStop { position: 0.0; color: "#98A3B4" }
GradientStop { position: 1.0; color: "#5A6B83" }
}
}
}
GridView {
id: view
cellWidth: width / colCount
cellHeight: 128 + Style.strutSize
highlightMoveDuration: 0
readonly property int baseCellWidth: 172 + (Style.strutSize * 2)
readonly property int colCount: Math.floor(width / baseCellWidth)
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
right: scrollbar.left
topMargin: Style.margin
}
delegate: AircraftGridDelegate {
width: view.cellWidth
height: view.cellHeight
onSelect: {
view.currentIndex = model.index;
_launcher.selectedAircraft = uri;
}
onShowDetails: root.showDetails(uri)
}
clip: true
focus: true
highlight: highlight
}
Scrollbar {
id: scrollbar
anchors.right: parent.right
anchors.top: parent.top
height: view.height
flickable: view
}
}

View file

@ -6,8 +6,15 @@ FocusScope
{
id: root
Component.onCompleted: {
aircraftList.updateSelectionFromLauncher();
property var __model: null
property Component __header: null
property string __lastState: "installed"
function updateSelectionFromLauncher()
{
if (aircraftContent.item) {
aircraftContent.item.updateSelectionFromLauncher();
}
}
Rectangle
@ -16,6 +23,14 @@ FocusScope
height: searchButton.height + (Style.margin * 2)
width: parent.width
GridToggleButton {
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: Style.margin
gridMode: !_launcher.aircraftGridMode
onClicked: _launcher.aircraftGridMode = !_launcher.aircraftGridMode
}
Row {
anchors.centerIn: parent
spacing: Style.margin
@ -25,7 +40,7 @@ FocusScope
text: qsTr("Installed Aircraft")
onClicked: {
root.state = "installed"
aircraftList.updateSelectionFromLauncher();
root.updateSelectionFromLauncher();
}
active: root.state == "installed"
}
@ -35,7 +50,7 @@ FocusScope
text: qsTr("Browse")
onClicked: {
root.state = "browse"
aircraftList.updateSelectionFromLauncher();
root.updateSelectionFromLauncher();
}
active: root.state == "browse"
}
@ -46,7 +61,7 @@ FocusScope
text: qsTr("Updates")
onClicked: {
root.state = "updates"
aircraftList.updateSelectionFromLauncher();
root.updateSelectionFromLauncher();
}
active: root.state == "updates"
}
@ -64,7 +79,7 @@ FocusScope
onSearch: {
_launcher.searchAircraftModel.setAircraftFilterString(term)
root.state = "search"
aircraftList.updateSelectionFromLauncher();
root.updateSelectionFromLauncher();
}
active: root.state == "search"
@ -80,90 +95,65 @@ FocusScope
anchors.top: tabBar.bottom
}
Component {
id: highlight
Rectangle {
gradient: Gradient {
GradientStop { position: 0.0; color: "#98A3B4" }
GradientStop { position: 1.0; color: "#5A6B83" }
}
}
}
Component {
id: ratingsHeader
AircraftRatingsPanel {
width: aircraftList.width - Style.strutSize * 2
x: (aircraftList.width - width) / 2
theList: aircraftList
width: aircraftContent.width
onClearSelection: {
_launcher.selectedAircraft = "";
root.updateSelectionFromLauncher()
}
}
}
Component {
id: noDefaultCatalogHeader
NoDefaultCatalogPanel {
width: aircraftList.width - Style.strutSize * 2
x: (aircraftList.width - width) / 2
width: aircraftContent.width
}
}
Component {
id: updateAllHeader
UpdateAllPanel {
width: aircraftList.width - Style.strutSize * 2
x: (aircraftList.width - width) / 2
width: aircraftContent.width
}
}
ListView {
id: aircraftList
Component {
id: emptyHeader
Item {
}
}
Loader {
id: aircraftContent
source: _launcher.aircraftGridMode ? "qrc:///qml/AircraftGridView.qml"
: "qrc:///qml/AircraftListView.qml"
anchors {
left: parent.left
top: tabBarDivider.bottom
bottom: parent.bottom
right: scrollbar.left
right: parent.right
topMargin: Style.margin
}
delegate: AircraftCompactDelegate {
onSelect: {
aircraftList.currentIndex = model.index;
_launcher.selectedAircraft = uri;
}
onShowDetails: root.showDetails(uri)
Binding {
target: aircraftContent.item
property: "model"
value: root.__model
}
clip: true
focus: true
Binding {
target: aircraftContent.item
property: "header"
value: root.__header
}
// prevent mouse wheel interactions when the details view is
// visible, since it has its own flickable
enabled: !detailsView.visible
highlight: highlight
highlightMoveDuration: __realHighlightMoveDuration
// saved here becuase we need to reset highlightMoveDuration
// when doing a progrmatic set
readonly property int __realHighlightMoveDuration: 200
function updateSelectionFromLauncher()
{
model.selectVariantForAircraftURI(_launcher.selectedAircraft);
var row = model.indexForURI(_launcher.selectedAircraft);
if (row >= 0) {
// sequence here is necessary so progrommatic moves
// are instant
highlightMoveDuration = 0;
currentIndex = row;
highlightMoveDuration = __realHighlightMoveDuration;
} else {
// clear selection in view, so we don't show something
// erroneous such as the previous value
currentIndex = -1;
}
Connections {
target: aircraftContent.item
onShowDetails: root.showDetails(uri)
}
}
@ -173,7 +163,7 @@ FocusScope
left: parent.left
top: tabBar.bottom
bottom: parent.bottom
right: scrollbar.left
right: parent.right
}
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
@ -182,49 +172,42 @@ FocusScope
visible: (root.state == "updates") && (_launcher.aircraftWithUpdatesModel.count == 0)
}
Scrollbar {
id: scrollbar
anchors.right: parent.right
anchors.top: tabBar.bottom
height: aircraftList.height
flickable: aircraftList
}
state: "installed"
states: [
State {
name: "installed"
PropertyChanges {
target: aircraftList
model: _launcher.installedAircraftModel
target: root
__model: _launcher.installedAircraftModel
__header: emptyHeader
}
},
State {
name: "search"
PropertyChanges {
target: aircraftList
model: _launcher.searchAircraftModel
header: null
target: root
__model: _launcher.searchAircraftModel
__header: emptyHeader
}
},
State {
name: "browse"
PropertyChanges {
target: aircraftList
model: _launcher.browseAircraftModel
header: _addOns.showNoOfficialHangar ? noDefaultCatalogHeader : ratingsHeader
target: root
__model: _launcher.browseAircraftModel
__header: _addOns.showNoOfficialHangar ? noDefaultCatalogHeader : ratingsHeader
}
},
State {
name: "updates"
PropertyChanges {
target: aircraftList
model: _launcher.aircraftWithUpdatesModel
header: (_launcher.aircraftWithUpdatesModel.count > 0) ? updateAllHeader : null
target: root
__model: _launcher.aircraftWithUpdatesModel
__header: (_launcher.aircraftWithUpdatesModel.count > 0) ? updateAllHeader : emptyHeader
}
}
]
@ -239,6 +222,8 @@ FocusScope
function goBack()
{
// details view can change the aircraft URI / variant
updateSelectionFromLauncher();
detailsView.visible = false;
}
@ -250,15 +235,9 @@ FocusScope
Button {
anchors { left: parent.left; top: parent.top; margins: Style.margin }
width: Style.strutSize
id: backButton
text: "< Back"
onClicked: {
// ensure that if the variant was changed inside the detailsView,
// that we update our selection correctly
aircraftList.updateSelectionFromLauncher();
root.goBack();
}
onClicked: root.goBack();
}
}
}

View file

@ -0,0 +1,80 @@
import QtQuick 2.0
import FlightGear.Launcher 1.0 as FG
import "."
Item {
id: root
property alias model: aircraftList.model
property alias header: aircraftList.header
signal showDetails(var uri);
function updateSelectionFromLauncher()
{
model.selectVariantForAircraftURI(_launcher.selectedAircraft);
var row = model.indexForURI(_launcher.selectedAircraft);
if (row >= 0) {
// sequence here is necessary so progrommatic moves
// are instant
aircraftList.highlightMoveDuration = 0;
aircraftList.currentIndex = row;
aircraftList.highlightMoveDuration = aircraftList.__realHighlightMoveDuration;
} else {
// clear selection in view, so we don't show something
// erroneous such as the previous value
aircraftList.currentIndex = -1;
}
}
onModelChanged: updateSelectionFromLauncher()
Component {
id: highlight
Rectangle {
gradient: Gradient {
GradientStop { position: 0.0; color: "#98A3B4" }
GradientStop { position: 1.0; color: "#5A6B83" }
}
}
}
ListView {
id: aircraftList
anchors {
left: parent.left
top: parent.top
bottom: parent.bottom
right: scrollbar.left
topMargin: Style.margin
}
delegate: AircraftCompactDelegate {
onSelect: {
aircraftList.currentIndex = model.index;
_launcher.selectedAircraft = uri;
}
onShowDetails: root.showDetails(uri)
}
clip: true
focus: true
highlight: highlight
highlightMoveDuration: __realHighlightMoveDuration
// saved here becuase we need to reset highlightMoveDuration
// when doing a progrmatic set
readonly property int __realHighlightMoveDuration: 200
}
Scrollbar {
id: scrollbar
anchors.right: parent.right
anchors.top: parent.top
height: aircraftList.height
flickable: aircraftList
}
}

View file

@ -4,7 +4,8 @@ import "."
ListHeaderBox
{
property ListView theList: null
id: root
signal clearSelection();
contents: [
@ -39,7 +40,7 @@ ListHeaderBox
onClicked: {
// clear selection so we don't jump to the selected item
// each time the proxy model changes.
theList.currentIndex = -1;
root.clearSelection();
editRatingsPanel.visible = true
}

View file

@ -10,11 +10,13 @@ Rectangle {
implicitHeight: title.implicitHeight
radius: 4
radius: Style.roundRadius
border.color: Style.frameColor
border.width: headingMouseArea.containsMouse ? 1 : 0
color: headingMouseArea.containsMouse ? "#7fffffff" : "transparent"
readonly property int centerX: width / 2
readonly property bool __enabled: aircraftInfo.numVariants > 1
property alias aircraft: aircraftInfo.uri
@ -28,33 +30,53 @@ Rectangle {
signal selected(var index)
Text {
id: title
anchors.verticalCenter: parent.verticalCenter
anchors.right: upDownIcon.left
anchors.left: parent.left
anchors.leftMargin: 4
anchors.rightMargin: 4
horizontalAlignment: Text.AlignHCenter
font.pixelSize: Style.baseFontPixelSize * 2
text: aircraftInfo.name
fontSizeMode: Text.Fit
elide: Text.ElideRight
maximumLineCount: 1
color: headingMouseArea.containsMouse ? Style.themeColor : Style.baseTextColor
}
Image {
id: upDownIcon
source: "qrc:///up-down-arrow"
anchors.right: parent.right
anchors.rightMargin: 8
anchors.verticalCenter: parent.verticalCenter
visible: __enabled
// Image {
// id: upDownIcon
// source: "qrc:///up-down-arrow"
// x: root.centerX + Math.min(title.implicitWidth * 0.5, title.width * 0.5)
// anchors.verticalCenter: parent.verticalCenter
// visible: __enabled
// }
Row {
anchors.centerIn: parent
height: title.implicitHeight
width: childrenRect.width
Text {
id: title
anchors.verticalCenter: parent.verticalCenter
// this ugly logic is to keep the up-down arrow at the right
// hand side of the text, but allow the font scaling to work if
// we're short on horizontal space
width: Math.min(implicitWidth,
root.width - (Style.margin * 2) - (__enabled ? upDownIcon.implicitWidth : 0))
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
font.pixelSize: Style.baseFontPixelSize * 2
text: aircraftInfo.name
fontSizeMode: Text.Fit
elide: Text.ElideRight
maximumLineCount: 1
color: headingMouseArea.containsMouse ? Style.themeColor : Style.baseTextColor
}
Image {
id: upDownIcon
source: "qrc:///up-down-arrow"
// x: root.centerX + Math.min(title.implicitWidth * 0.5, title.width * 0.5)
anchors.verticalCenter: parent.verticalCenter
visible: __enabled
width: __enabled ? implicitWidth : 0
}
}
MouseArea {

View file

@ -0,0 +1,43 @@
import QtQuick 2.0
import "."
Rectangle {
id: root
radius: Style.roundRadius
border.width: 1
border.color: Style.themeColor
width: height
height: Style.baseFontPixelSize + Style.margin * 2
color: mouse.containsMouse ? Style.minorFrameColor : "white"
property bool gridMode: false
signal clicked();
Image {
id: icon
width: parent.width - Style.margin
height: parent.height - Style.margin
anchors.centerIn: parent
source: root.gridMode ? "qrc:///svg/icon-grid-view"
: "qrc:///svg/icon-list-view"
}
MouseArea {
id: mouse
hoverEnabled: true
onClicked: root.clicked();
anchors.fill: parent
}
Text {
anchors.left: root.right
anchors.leftMargin: Style.margin
anchors.verticalCenter: root.verticalCenter
visible: mouse.containsMouse
color: Style.baseTextColor
font.pixelSize: Style.baseFontPixelSize
text: root.gridMode ? qsTr("Switch to grid view")
: qsTr("Switch to list view")
}
}

View file

@ -11,7 +11,8 @@ Item {
Rectangle {
id: contentBox
width: parent.width
width: parent.width - Style.strutSize * 2
x: Style.strutSize
height: Style.strutSize
y: Style.margin
color: "white"

View file

@ -123,6 +123,10 @@
<file>qml/RouteLegsView.qml</file>
<file>qml/OverlayMenu.qml</file>
<file>qml/PopupChoiceItem.qml</file>
<file>qml/AircraftGridDelegate.qml</file>
<file>qml/AircraftGridView.qml</file>
<file>qml/AircraftListView.qml</file>
<file>qml/GridToggleButton.qml</file>
</qresource>
<qresource prefix="/preview">
<file alias="close-icon">preview-close.png</file>
@ -139,5 +143,7 @@
<file alias="toolbox-aircraft">assets/icons8-aircraft.svg</file>
<file alias="toolbox-fly-alt">assets/icons8-rocket.svg</file>
<file alias="toolbox-fly-heli">assets/icons8-helicopter.svg</file>
<file alias="icon-grid-view">assets/icons8-grid-view.svg</file>
<file alias="icon-list-view">assets/icons8-menu.svg</file>
</qresource>
</RCC>