diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index 31100c9..87d2464 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -1,110 +1,111 @@ set(itinerary_srcs countryinformation.cpp pkpassmanager.cpp reservationmanager.cpp timelinemodel.cpp ) ecm_qt_declare_logging_category(itinerary_srcs HEADER logging.h IDENTIFIER Log CATEGORY_NAME org.kde.itinerary ) add_library(itinerary STATIC ${itinerary_srcs}) target_link_libraries(itinerary PUBLIC KPim::Itinerary KPim::PkPass KF5::I18n Qt5::Network ) add_executable(itinerary-app main.cpp applicationcontroller.cpp localizer.cpp pkpassimageprovider.cpp settings.cpp qml.qrc ) target_include_directories(itinerary-app PRIVATE ${CMAKE_BINARY_DIR}) target_link_libraries(itinerary-app PRIVATE itinerary itinerary-weather Qt5::Quick KF5::Contacts ) if (ANDROID) # explicitly add runtime dependencies and transitive link dependencies, # so androiddeployqt picks them up target_link_libraries(itinerary-app PRIVATE KF5::Archive KF5::Kirigami2 Qt5::AndroidExtras Qt5::Svg KF5::Prison ) kirigami_package_breeze_icons(ICONS document-open edit-delete go-next-symbolic map-symbolic settings-configure view-calendar-day view-refresh weather-clear weather-clear-night weather-few-clouds weather-few-clouds-night weather-clouds weather-clouds-night weather-showers-day weather-showers-night weather-showers-scattered-day weather-showers-scattered-night weather-snow-scattered-day weather-snow-scattered-night weather-storm-day weather-storm-night weather-many-clouds weather-fog weather-showers weather-showers-scattered weather-hail weather-snow weather-snow-scattered weather-storm ) else () target_link_libraries(itinerary-app PRIVATE Qt5::Positioning) set_target_properties(itinerary-app PROPERTIES OUTPUT_NAME "itinerary") endif() qml_lint( main.qml BoardingPass.qml BusDelegate.qml BusPage.qml CountryInfoDelegate.qml DetailsPage.qml FlightDelegate.qml FlightPage.qml HotelDelegate.qml HotelPage.qml PkPassPage.qml PlaceDelegate.qml RestaurantDelegate.qml RestaurantPage.qml SettingsPage.qml TicketTokenDelegate.qml TimelineDelegate.qml TimelinePage.qml TouristAttractionDelegate.qml TrainDelegate.qml TrainPage.qml + WeatherForecastDelegate.qml ) install(TARGETS itinerary-app ${INSTALL_TARGETS_DEFAULT_ARGS}) if (NOT ANDROID) install(PROGRAMS org.kde.itinerary.desktop DESTINATION ${KDE_INSTALL_APPDIR}) endif() diff --git a/src/app/TimelinePage.qml b/src/app/TimelinePage.qml index 9cfaa99..45f8074 100644 --- a/src/app/TimelinePage.qml +++ b/src/app/TimelinePage.qml @@ -1,155 +1,162 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ import QtQuick 2.5 import QtQuick.Layouts 1.1 import QtQuick.Controls 2.1 as QQC2 import org.kde.kirigami 2.4 as Kirigami import org.kde.itinerary 1.0 import "." as App Kirigami.ScrollablePage { id: root title: qsTr("My Itinerary") // context drawer content actions { contextualActions: [ Kirigami.Action { text: qsTr("Today") iconName: "view-calendar-day" onTriggered: listView.positionViewAtIndex(_timelineModel.todayRow, ListView.Beginning); } ] } // page content Component { id: flightDelegate App.FlightDelegate { resId: modelData.reservationId reservation: modelData.reservation passId: modelData.passId rangeType: modelData.rangeType } } Component { id: hotelDelegate App.HotelDelegate { resId: modelData.reservationId reservation: modelData.reservation passId: modelData.passId rangeType: modelData.rangeType } } Component { id: trainDelegate App.TrainDelegate { resId: modelData.reservationId reservation: modelData.reservation passId: modelData.passId rangeType: modelData.rangeType } } Component { id: busDelegate App.BusDelegate { resId: modelData.reservationId reservation: modelData.reservation passId: modelData.passId rangeType: modelData.rangeType } } Component { id: restaurantDelegate App.RestaurantDelegate { resId: modelData.reservationId reservation: modelData.reservation passId: modelData.passId rangeType: modelData.rangeType } } Component { id: touristAttractionDelegate App.TouristAttractionDelegate { resId: modelData.reservationId reservation: modelData.reservation passId: modelData.passId rangeType: modelData.rangeType } } Component { id: todayDelegate Item { implicitHeight: visible ? label.implicitHeight : 0 visible: modelData.isTodayEmpty QQC2.Label { id: label anchors.fill: parent text: qsTr("Nothing on the itinerary for today."); color: Kirigami.Theme.textColor horizontalAlignment: Qt.AlignHCenter } } } Component { id: countryInfoDelegate App.CountryInfoDelegate { countryInfo: modelData.countryInformation } } + Component { + id: weatherForecastDelegate + App.WeatherForecastDelegate { + weatherForecast: modelData.weatherForecast + } + } Kirigami.CardsListView { id: listView model: _timelineModel delegate: Loader { property var modelData: model height: item ? item.implicitHeight : 0 sourceComponent: { if (!modelData) return; switch (modelData.type) { case TimelineModel.Flight: return flightDelegate; case TimelineModel.Hotel: return hotelDelegate; case TimelineModel.TrainTrip: return trainDelegate; case TimelineModel.BusTrip: return busDelegate; case TimelineModel.Restaurant: return restaurantDelegate; case TimelineModel.TouristAttraction: return touristAttractionDelegate; case TimelineModel.TodayMarker: return todayDelegate; case TimelineModel.CountryInfo: return countryInfoDelegate; + case TimelineModel.WeatherForecast: return weatherForecastDelegate; } } } section.property: "sectionHeader" section.delegate: Item { implicitHeight: headerItem.implicitHeight + Kirigami.Units.largeSpacing*2 implicitWidth: ListView.view.width Kirigami.BasicListItem { id: headerItem label: section backgroundColor: Kirigami.Theme.backgroundColor icon: "view-calendar-day" } } section.criteria: ViewSection.FullString section.labelPositioning: ViewSection.CurrentLabelAtStart | ViewSection.InlineLabels } Component.onCompleted: listView.positionViewAtIndex(_timelineModel.todayRow, ListView.Beginning); } diff --git a/src/app/WeatherForecastDelegate.qml b/src/app/WeatherForecastDelegate.qml new file mode 100644 index 0000000..acf6cfb --- /dev/null +++ b/src/app/WeatherForecastDelegate.qml @@ -0,0 +1,64 @@ +/* + Copyright (C) 2018 Volker Krause + + This program is free software; you can redistribute it and/or modify it + under the terms of the GNU Library General Public License as published by + the Free Software Foundation; either version 2 of the License, or (at your + option) any later version. + + This program is distributed in the hope that it will be useful, but WITHOUT + ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public + License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +*/ + +import QtQuick 2.5 +import QtQuick.Layouts 1.1 +import QtQuick.Controls 2.1 as QQC2 +import org.kde.kirigami 2.4 as Kirigami +import org.kde.itinerary 1.0 +import "." as App + +Kirigami.AbstractCard { + id: root + property var weatherForecast + visible: weatherForecast.valid + headerOrientation: Qt.Horizontal + + header: Rectangle { + id: headerBackground + Kirigami.Theme.colorSet: Kirigami.Theme.Complementary + Kirigami.Theme.inherit: false + color: Kirigami.Theme.backgroundColor + implicitWidth: icon.implicitWidth + Kirigami.Units.largeSpacing * 2 + Layout.minimumHeight: implicitWidth + Layout.fillHeight: true + anchors.leftMargin: -root.leftPadding + anchors.topMargin: -root.topPadding + anchors.bottomMargin: -root.rightPadding + + Kirigami.Icon { + id: icon + anchors.fill: parent + anchors.margins: Kirigami.Units.largeSpacing + source: weatherForecast.symbolIconName + } + } + + contentItem: ColumnLayout { + Layout.fillWidth: true + QQC2.Label { + text: qsTr("Temperature: %1 °C").arg(weatherForecast.temperature) + color: Kirigami.Theme.textColor + Layout.fillWidth: true + } + QQC2.Label { + text: qsTr("Precipitation: %1 mm").arg(weatherForecast.precipitation) + color: Kirigami.Theme.textColor + Layout.fillWidth: true + } + } +} diff --git a/src/app/qml.qrc b/src/app/qml.qrc index 8a5f5bf..b70db81 100644 --- a/src/app/qml.qrc +++ b/src/app/qml.qrc @@ -1,25 +1,26 @@ main.qml BoardingPass.qml BusDelegate.qml BusPage.qml CountryInfoDelegate.qml DetailsPage.qml FlightDelegate.qml FlightPage.qml HotelDelegate.qml HotelPage.qml PkPassPage.qml PlaceDelegate.qml RestaurantDelegate.qml RestaurantPage.qml SettingsPage.qml TicketTokenDelegate.qml TimelineDelegate.qml TimelinePage.qml TouristAttractionDelegate.qml TrainDelegate.qml TrainPage.qml + WeatherForecastDelegate.qml diff --git a/src/app/timelinemodel.h b/src/app/timelinemodel.h index bc7725b..f2ff721 100644 --- a/src/app/timelinemodel.h +++ b/src/app/timelinemodel.h @@ -1,101 +1,102 @@ /* Copyright (C) 2018 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU General Public License along with this program. If not, see . */ #ifndef TIMELINEMODEL_H #define TIMELINEMODEL_H #include #include class ReservationManager; class TimelineModel : public QAbstractListModel { Q_OBJECT Q_PROPERTY(int todayRow READ todayRow NOTIFY todayRowChanged) public: enum Role { PassIdRole = Qt::UserRole + 1, SectionHeader, ReservationRole, ReservationIdRole, ElementTypeRole, TodayEmptyRole, IsTodayRole, ElementRangeRole, CountryInformationRole }; enum ElementType { Undefined, Flight, TrainTrip, BusTrip, Hotel, Restaurant, TouristAttraction, TodayMarker, - CountryInfo + CountryInfo, + WeatherForecast }; Q_ENUM(ElementType) // indicates whether an element is self-contained or the beginning/end of a longer timespan/range enum RangeType { SelfContained, RangeBegin, RangeEnd }; Q_ENUM(RangeType) explicit TimelineModel(QObject *parent = nullptr); ~TimelineModel(); void setReservationManager(ReservationManager *mgr); QVariant data(const QModelIndex& index, int role) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QHash roleNames() const override; int todayRow() const; signals: void todayRowChanged(); private: struct Element { QString id; // reservation id QVariant content; // non-reservation content QDateTime dt; // relevant date/time ElementType elementType; RangeType rangeType; }; void reservationAdded(const QString &resId); void insertElement(Element &&elem); void reservationUpdated(const QString &resId); void updateElement(const QString &resId, const QVariant &res, RangeType rangeType); void reservationRemoved(const QString &resId); void updateInformationElements(); std::vector::iterator erasePreviousCountyInfo(std::vector::iterator it); ReservationManager *m_resMgr = nullptr; std::vector m_elements; }; #endif // TIMELINEMODEL_H