diff --git a/src/apps/marble-maps/Bookmarks.qml b/src/apps/marble-maps/Bookmarks.qml new file mode 100644 index 000000000..a358ef366 --- /dev/null +++ b/src/apps/marble-maps/Bookmarks.qml @@ -0,0 +1,135 @@ +// +// This file is part of the Marble Virtual Globe. +// +// This program is free software licensed under the GNU LGPL. You can +// find a copy of this license in LICENSE.txt in the top directory of +// the source code. +// +// Copyright 2016 Dennis Nienhüser +// + +import QtQuick 2.4 +import QtQuick.Controls 1.4 +import QtQuick.Window 2.2 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.0 + +import org.kde.kirigami 2.0 as Kirigami + +import org.kde.marble 0.20 +Kirigami.ScrollablePage { + id: root + property int currentIndex: 0 + property var marbleQuickItem: null + + SystemPalette{ + id: palette + colorGroup: SystemPalette.Active + } + + Rectangle { + anchors.fill: parent + color: palette.base + } + + Column { + id: column + anchors { + fill: parent + margins: Screen.pixelDensity * 2 + } + + Text { + text: "

Bookmarks

" + } + + ListView { + id: bookmarksView + width: parent.width + interactive: false + spacing: 5 + height: contentHeight + model: bookmarks.model + + delegate: + Rectangle { + width: parent.width + height: Screen.pixelDensity * 2 + Math.max(text.height, imageButton.height) + color: palette.base + + Image { + id: imageButton + anchors { + left: parent.left + verticalCenter: parent.verticalCenter + } + source: "qrc:///material/place.svg" + width: Screen.pixelDensity * 6 + height: width + smooth: true + } + + ColorOverlay{ + anchors.fill: imageButton + source: imageButton + color: palette.highlight + } + + Text { + id: text + anchors { + left: imageButton.right + leftMargin: parent.width * 0.05 + verticalCenter: parent.verticalCenter + } + elide: Text.ElideMiddle + text: model.display + font.pointSize: 12 + color: palette.text + } + + + Image { + id: upButton + anchors{ + verticalCenter: parent.verticalCenter + right: parent.right + } + width: Screen.pixelDensity * 6 + height: width + source: "qrc:///material/drag.png" + smooth: true + } + } + } + + Column { + visible: bookmarksView.model.count === 0 + width: parent.width + + Text { + width: 0.8 * parent.width + font.pointSize: 18 + color: paletteDisabled.text + text: qsTr("Your bookmarks will appear here.") + wrapMode: Text.WrapAtWordBoundaryOrAnywhere + elide: Text.ElideRight + } + + Image { + anchors.right: parent.right + anchors.bottom: root.bottom + width: 0.3 * parent.width + fillMode: Image.PreserveAspectFit + source: "qrc:/konqi/books.png" + } + } + } + + Bookmarks { + id: bookmarks + map: root.marbleQuickItem + } +} + + diff --git a/src/apps/marble-maps/MainScreen.qml b/src/apps/marble-maps/MainScreen.qml index 64935b55c..c073c7fbc 100644 --- a/src/apps/marble-maps/MainScreen.qml +++ b/src/apps/marble-maps/MainScreen.qml @@ -1,590 +1,611 @@ // // This file is part of the Marble Virtual Globe. // // This program is free software licensed under the GNU LGPL. You can // find a copy of this license in LICENSE.txt in the top directory of // the source code. // // Copyright 2015 Gábor Péterffy // Copyright 2015 Dennis Nienhüser // Copyright 2015 Mikhail Ivchenko // import QtQuick 2.3 import QtQuick.Controls 2.0 import QtQuick.Window 2.2 import org.kde.marble 0.20 import org.kde.kirigami 2.0 as Kirigami Kirigami.ApplicationWindow { id: app title: qsTr("Marble Maps") visible: true width: 600 height: 400 color: "#f9f9f9" // Keep the background white while no dialog is loaded property alias state: stateTracker.state property var selectedPlacemark property bool showOsmTags: false property int currentWaypointIndex: 0 property real animatedMargin: app.state === "none" ? 0 : -dialogLoader.height property bool dialogExpanded: animatedMargin === -dialogLoader.height property real mapOffset: !dialogExpanded ? animatedMargin / 2 : 0 Behavior on animatedMargin { NumberAnimation { id: dialogAnimation duration: 200 easing.type: Easing.OutQuart } } onSelectedPlacemarkChanged: { if (!selectedPlacemark) { app.state = "none" } } SystemPalette{ id: palette colorGroup: SystemPalette.Active } Settings { id: settings } globalDrawer: Kirigami.GlobalDrawer { id: sidePanel title: "Settings" handleVisible: false property alias showAccessibility: accessibilityAction.checked Settings { id: sidePanelSettings property bool showUpdateInfo: Number(value("MarbleMaps", "updateInfoVersion", "0")) < 1 Component.onDestruction: { sidePanelSettings.setValue("MarbleMaps", "showAccessibility", accessibilityAction.checked ? "true" : "false") } } actions: [ Kirigami.Action { id: publicTransportAction text: qsTr("Public Transport") checkable: true checked: marbleMaps.showPublicTransport iconName: "qrc:///material/directions-bus.svg" visible: true onTriggered: { sidePanel.close() marbleMaps.showPublicTransport = checked publicTransportDialog.open() } }, Kirigami.Action { id: outdoorActivitiesAction checkable: true checked: marbleMaps.showOutdoorActivities text: qsTr("Outdoor Activities") visible: true iconName: "qrc:///material/directions-run.svg" onTriggered: { sidePanel.close() marbleMaps.showOutdoorActivities = checked } }, Kirigami.Action { id: accessibilityAction checkable: true checked: settings.value("MarbleMaps", "showAccessibility", "false") === "true" text: qsTr("Accessibility") visible: true iconName: "qrc:///material/wheelchair.svg" onTriggered: { sidePanelSettings.value("MarbleMaps", "showAccessibility", "false") === "true" } }, Kirigami.Action{ enabled: false}, Kirigami.Action { text: "About" iconName: "qrc:///marble.svg" visible: true onTriggered: { app.state = "about" sidePanel.close() source = "" app.pageStack.push("qrc:///AboutDialog.qml") } }, + Kirigami.Action { + text: "Bookmarks" + iconName: "qrc:///material/star.svg" + onTriggered: { + app.state = "bookmarks" + sidePanel.close() + app.pageStack.push("qrc:///Bookmarks.qml") + } + }, Kirigami.Action { text: "Layer Options" iconName: "qrc:///settings.png" onTriggered: { app.state = "options" sidePanel.close() app.pageStack.push("qrc:///Options.qml") } } ] + + Binding { + target: pageStack.currentItem + property: "marbleQuickItem" + value: marbleMaps + when: app.state === "bookmarks" + } } pageStack.initialPage: page pageStack.interactive: false Kirigami.Page { id: page padding: 0 topPadding: 0 leftPadding: 0 rightPadding: 0 bottomPadding: 0 title: "Marble Maps" Item { id: mapItem anchors { top: parent.top topMargin: mapOffset left: parent.left right: parent.right } height: !dialogExpanded ? parent.height : parent.height + animatedMargin PinchArea { anchors.fill: parent enabled: true onPinchStarted: marbleMaps.handlePinchStarted(pinch.center) onPinchFinished: marbleMaps.handlePinchFinished(pinch.center) onPinchUpdated: marbleMaps.handlePinchUpdated(pinch.center, pinch.scale); MarbleMaps { id: marbleMaps property string currentPositionProvider: "QtPositioning" property bool wlanOnly: false property bool smallZoom : radius < 2 * Math.max(app.width, app.height) anchors.fill: parent visible: true // Theme settings. projection: smallZoom ? MarbleItem.Spherical : MarbleItem.Mercator mapThemeId: settings.value("MarbleMaps", "mapThemeId", "earth/vectorosm/vectorosm.dgml") // Visibility of layers/plugins. showFrameRate: false showAtmosphere: smallZoom showCompass: false showClouds: false showCrosshairs: false showGrid: smallZoom showOverviewMap: false showOtherPlaces: false showScaleBar: false showBackground: smallZoom showPublicTransport: settings.value("MarbleMaps", "showPublicTransport", "false") === "true" positionProvider: suspended ? "" : currentPositionProvider keepScreenOn: !suspended && navigationManager.guidanceModeEnabled showPositionMarker: false animationViewContext: dialogAnimation.running placemarkDelegate: Image { id: balloon property int xPos: 0 property int yPos: 0 property real animationOffset: 0 property var placemark: null x: xPos - 0.5 * width y: yPos - height - 30 * Screen.pixelDensity * animationOffset opacity: 1.0 - animationOffset Connections { target: app onSelectedPlacemarkChanged: balloonAnimation.restart() } NumberAnimation { id: balloonAnimation target: balloon property: "animationOffset" from: 1 to: 0 duration: 1000 easing.type: Easing.OutBounce } width: Screen.pixelDensity*6 height: width source: "qrc:///ic_place.png" onPlacemarkChanged: { app.selectedPlacemark = placemark if (placemark) { app.state = "place" } else { app.state = "none" } } } onPositionAvailableChanged: { updateIndicator(); } onPositionVisibleChanged: { updateIndicator(); } onVisibleLatLonAltBoxChanged: { !panningDetectionTimer.restart(); updateIndicator(); } onCurrentPositionChanged: { updateIndicator(); } onZoomChanged: { zoomDetectionTimer.restart() } Component.onCompleted: { setPluginSetting("coordinate-grid", "gridColor", "#999999"); setPluginSetting("coordinate-grid", "tropicsColor", "#888888"); setPluginSetting("coordinate-grid", "equatorColor", "#777777"); setPluginSetting("coordinate-grid", "primaryLabels", "false"); setPluginSetting("coordinate-grid", "secondaryLabels", "false"); marbleMaps.loadSettings() } Component.onDestruction: marbleMaps.writeSettings() Connections { target: Qt.application onStateChanged: { if (Qt.application.state === Qt.ApplicationInactive || Qt.application.state === Qt.ApplicationSuspended) { marbleMaps.writeSettings() } } } function updateIndicator() { if ( !positionVisible && positionAvailable ) { zoomToPositionButton.updateIndicator(); } } RoutingManager { id: routingManager anchors.fill: parent marbleItem: marbleMaps visible: hasRoute function addToRoute() { ensureRouteHasDeparture() routingManager.addViaByPlacemarkAtIndex(routingManager.waypointCount(), selectedPlacemark) routingManager.clearSearchResultPlacemarks() selectedPlacemark = null app.state = "route" } function ensureRouteHasDeparture() { if (routingManager.routeRequestModel.count === 0) { if (marbleMaps.positionAvailable) { routingManager.addViaByPlacemark(marbleMaps.currentPosition) } } } } Timer { id: zoomDetectionTimer interval: 1000 } Timer { id: panningDetectionTimer interval: 1000 } PositionMarker { id: positionMarker x: navigationManager.snappedPositionMarkerScreenPosition.x - positionMarker.width / 2 y: navigationManager.snappedPositionMarkerScreenPosition.y - positionMarker.height / 2 angle: marbleMaps.angle visible: marbleMaps.positionAvailable && marbleMaps.positionVisible radius: navigationManager.screenAccuracy / 2 showAccuracy: navigationManager.deviated allowRadiusAnimation: !zoomDetectionTimer.running allowPositionAnimation: !panningDetectionTimer.running speed: marbleMaps.speed MouseArea { anchors.fill: parent onPressed: app.state = "position" } } MouseArea { anchors.fill: parent propagateComposedEvents: true onPressed: { marbleMaps.focus = true; mouse.accepted = false; } } } NavigationManager { id: navigationManager width: parent.width height: parent.height visible: false marbleItem: marbleMaps hasRoute: routingManager.hasRoute } } BoxedText { id: distanceIndicator text: qsTr("%1 km").arg(zoomToPositionButton.distance < 10 ? zoomToPositionButton.distance.toFixed(1) : zoomToPositionButton.distance.toFixed(0)) anchors { bottom: zoomToPositionButton.top horizontalCenter: zoomToPositionButton.horizontalCenter } visible: marbleMaps.positionAvailable && !marbleMaps.positionVisible } PositionButton { id: zoomToPositionButton anchors { right: parent.right rightMargin: Screen.pixelDensity * 1 bottom: routeEditorButton.top bottomMargin: 10 } enabled: marbleMaps.positionAvailable iconSource: marbleMaps.positionAvailable ? "qrc:///gps_fixed.png" : "qrc:///gps_not_fixed.png" onClicked: marbleMaps.centerOnCurrentPosition() property real distance: 0 function updateIndicator() { var point = marbleMaps.mapFromItem(zoomToPositionButton, diameter * 0.5, diameter * 0.5); distance = 0.001 * marbleMaps.distanceFromPointToCurrentLocation(point); angle = marbleMaps.angleFromPointToCurrentLocation(point); } showDirection: marbleMaps.positionAvailable && !marbleMaps.positionVisible } CircularButton { id: routeEditorButton property string currentProfileIcon: "qrc:///material/directions-car.svg" anchors { bottom: parent.bottom bottomMargin: Screen.pixelDensity * 4 - mapOffset horizontalCenter: zoomToPositionButton.horizontalCenter } enabled: app.state !== "route" || routingManager.hasRoute onClicked: { if (app.state === "route") { app.state = "none" navigationManager.visible = true } else if (app.state === "place") { app.state = "route" routingManager.addToRoute() } else { app.state = "route" navigationManager.visible = false } } iconSource: "qrc:///material/directions.svg"; states: [ State { name: "" PropertyChanges { target: routeEditorButton; iconSource: "qrc:///material/directions.svg"; } }, State { name: "routingAction" when: app.state === "route" PropertyChanges { target: routeEditorButton; iconSource: "qrc:///material/navigation.svg"; } }, State { name: "placeAction" when: app.state === "place" PropertyChanges { target: routeEditorButton; iconSource: currentProfileIcon } } ] } } Search { id: search anchors.fill: parent marbleQuickItem: marbleMaps visible: !navigationManager.visible onItemSelected: { if (routingManager) { routingManager.addSearchResultAsPlacemark(suggestedPlacemark); } app.selectedPlacemark = suggestedPlacemark; app.state = "place" } onMenuButtonClicked: sidePanel.open() } Loader { id: dialogLoader - focus: true + width: childrenRect.width + height : childrenRect.height anchors { left: parent.left right: parent.right top: parent.bottom topMargin: app.animatedMargin } onLoaded: { if (app.state === "place") { dialogLoader.item.map = marbleMaps dialogLoader.item.placemark = app.selectedPlacemark dialogLoader.item.showOsmTags = app.showOsmTags dialogLoader.item.showAccessibility = sidePanel.showAccessibility } else if (app.state === "route") { item.routingManager = routingManager item.routingProfile = routingManager.routingProfile item.currentIndex = Qt.binding(function() { return app.currentWaypointIndex }) } else if (app.state == "position") { dialogLoader.item.map = marbleMaps dialogLoader.item.navigationManager = navigationManager } } Connections { target: dialogLoader.item onCurrentProfileIconChanged: routeEditorButton.currentProfileIcon = dialogLoader.item.currentProfileIcon ignoreUnknownSignals: true } } BorderImage { visible: app.state != "none" anchors.fill: dialogLoader anchors.margins: -14 border { top: 14; left: 14; right: 14; bottom: 14 } source: "qrc:///border_shadow.png" } BoxedText { id: quitHelper visible: false text: qsTr("Press again to close.") anchors.bottom: parent.bottom anchors.bottomMargin: Screen.pixelDensity * 5 anchors.horizontalCenter: parent.horizontalCenter onVisibleChanged: { if (visible) { quitTimer.restart() } } Timer { id: quitTimer interval: 3000; running: false; repeat: false onTriggered: { app.aboutToQuit = false quitHelper.visible = false } } } Item { id: stateTracker state: "none" states: [ State { name: "none" PropertyChanges { target: dialogLoader; source: "" } }, State { name: "position" PropertyChanges { target: dialogLoader; source: "CurrentPosition.qml" } }, State { name: "route" PropertyChanges { target: dialogLoader; source: "RouteEditor.qml" } }, State { name: "place" PropertyChanges { target: dialogLoader; source: "PlacemarkDialog.qml" } }, State { name: "about" PropertyChanges { target: dialogLoader; source: "" } }, State { name: "settings" PropertyChanges { target: dialogLoader; source: "SettingsDialog.qml" } }, State { name: "developer" PropertyChanges { target: dialogLoader; source: "DeveloperDialog.qml" } }, State { name: "options" PropertyChanges { target: dialogLoader; source: "" } + }, + State { + name: "bookmarks" + PropertyChanges { target: dialogLoader; source: "" } } ] } } property bool aboutToQuit: false onClosing: { if (app.aboutToQuit === true) { close.accepted = true // we will quit return } else if (navigationManager.visible) { navigationManager.visible = false } else if (sidePanel.drawerOpen) { sidePanel.close() } else if (pageStack.depth > 1) { pageStack.pop() } else if (app.state !== "none") { app.state = "none" } else { app.aboutToQuit = true quitHelper.visible = true } close.accepted = false } } diff --git a/src/apps/marble-maps/MarbleMaps.qrc b/src/apps/marble-maps/MarbleMaps.qrc index deb0eddc7..8e236777b 100644 --- a/src/apps/marble-maps/MarbleMaps.qrc +++ b/src/apps/marble-maps/MarbleMaps.qrc @@ -1,95 +1,97 @@ AboutDialog.qml FlatButton.qml Completion.qml MainScreen.qml SearchResults.qml SearchField.qml Search.qml CircularButton.qml IconText.qml PositionButton.qml BoxedText.qml ImageButton.qml WaypointImage.qml RoutingManager.qml PlacemarkDialog.qml SettingsDialog.qml DeveloperDialog.qml ProfileSelectorMenu.qml FloatingMenuButton.qml RouteEditor.qml RoutesItem.qml MenuIcon.qml NavigationInfoBar.qml NavigationManager.qml PositionMarker.qml Waypoint.qml CurrentPosition.qml SidePanel.qml PublicTransport.qml OutdoorActivities.qml Options.qml + Bookmarks.qml drawer.svg ../../../data/android/drawable-xxxhdpi/search.png ../../../data/android/drawable-xxxhdpi/ic_menu_black_48dp.png ../../../data/android/drawable-xxxhdpi/gps_fixed.png ../../../data/android/drawable-xxxhdpi/gps_not_fixed.png ../../../data/android/drawable-xxxhdpi/backdrop.png ../../../data/android/drawable-xxxhdpi/border_shadow.png ../../../data/android/drawable-xxxhdpi/map.png ../../../data/android/drawable-xxxhdpi/waypoint.png ../../../data/android/drawable-xxxhdpi/up.png ../../../data/android/drawable-xxxhdpi/down.png ../../../data/android/drawable-xxxhdpi/delete.png ../../../data/android/drawable-xxxhdpi/delete_white.png ../../../data/android/drawable-xxxhdpi/ic_close_black_18dp.png ../../../data/android/drawable-xxxhdpi/busy_indicator.png ../../../data/android/drawable-xxxhdpi/navigation_blue.png ../../../data/svg/navigation_blue.svg ../../../data/android/drawable-xxxhdpi/circular_menu_backdrop.png ../../../data/android/drawable-xxxhdpi/ic_place.png ../../../data/android/drawable-xxxhdpi/ic_place_arrival.png ../../../data/android/drawable-xxxhdpi/ic_place_departure.png ../../../data/android/drawable-xxxhdpi/ic_place_unknown.png ../../../data/android/drawable-xxxhdpi/ic_place_via.png ../../../data/android/drawable-xxxhdpi/ic_add_black_48dp.png ../../../data/android/drawable-xxxhdpi/ic_settings_black_48dp.png icons/marble-lineart-simplified-logo.svg material-icons/ic_local_gas_station_48px.svg material-icons/ic_open_in_browser_48px.svg material-icons/ic_volume_off_48px.svg material-icons/ic_volume_up_48px.svg material-icons/ic_directions_48px.svg ../../../data/svg/material/maps/ic_directions_bike_48px.svg ../../../data/svg/material/maps/ic_directions_boat_48px.svg ../../../data/svg/material/maps/ic_directions_bus_48px.svg ../../../data/svg/material/maps/ic_directions_car_48px.svg ../../../data/svg/material/maps/ic_directions_railway_48px.svg ../../../data/svg/material/maps/ic_directions_run_48px.svg ../../../data/svg/material/maps/ic_directions_subway_48px.svg ../../../data/svg/material/maps/ic_directions_walk_48px.svg ../../../data/svg/material/maps/ic_tram_48px.svg ../../../data/svg/thenounproject/204712-hiker.svg ../../../data/svg/thenounproject/61698-mountain-biking.svg ../../../data/svg/thenounproject/101965-inline-skater.svg ../../../data/svg/thenounproject/78374-horse-riding.svg material-icons/ic_navigation_48px.svg material-icons/ic_access_time_48px.svg material-icons/ic_accessible_black_48px.svg material-icons/ic_network_wifi_black_48px.svg material-icons/ic_place_black_48px.svg material-icons/ic_star_24px.svg material-icons/ic_star_border_24px.svg material-icons/ic_label_48px.svg konqi/konqi-app-dev.png konqi/konqi-dev-qt.png konqi/konqi-group.png konqi/konqi-globe.png konqi/konqi-books.png RouteProfileRadioButton.qml MarbleScrollBar.qml material-icons/ic_phone_black_48px.svg + material-icons/ic_drag_handle_black_48dp.png diff --git a/src/apps/marble-maps/material-icons/ic_drag_handle_black_48dp.png b/src/apps/marble-maps/material-icons/ic_drag_handle_black_48dp.png new file mode 100644 index 000000000..b1fc31d5a Binary files /dev/null and b/src/apps/marble-maps/material-icons/ic_drag_handle_black_48dp.png differ