diff --git a/data/android/drawable-xxxhdpi/ic_menu_black_48dp.png b/data/android/drawable-xxxhdpi/ic_menu_black_48dp.png new file mode 100644 index 000000000..e92e6655d Binary files /dev/null and b/data/android/drawable-xxxhdpi/ic_menu_black_48dp.png differ diff --git a/src/apps/marble-maps/MainScreen.qml b/src/apps/marble-maps/MainScreen.qml index 016bc59e0..ea4572eb2 100644 --- a/src/apps/marble-maps/MainScreen.qml +++ b/src/apps/marble-maps/MainScreen.qml @@ -1,492 +1,493 @@ // // 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 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 } 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() 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: drawer.open() } Loader { id: dialogLoader focus: true 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 = drawer.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" } SidePanel { id: drawer width: 0.7 * app.width height: app.height marbleMaps: marbleMaps onAboutActionTriggered: { app.state = "about" dialogLoader.focus = true } } 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 } } } 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 (app.state !== "none") { app.state = "none" } else { app.aboutToQuit = true quitHelper.visible = true } close.accepted = 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: "AboutDialog.qml" } }, State { name: "settings" PropertyChanges { target: dialogLoader; source: "SettingsDialog.qml" } }, State { name: "developer" PropertyChanges { target: dialogLoader; source: "DeveloperDialog.qml" } } ] } } diff --git a/src/apps/marble-maps/MarbleMaps.qrc b/src/apps/marble-maps/MarbleMaps.qrc index 345429692..454c99897 100644 --- a/src/apps/marble-maps/MarbleMaps.qrc +++ b/src/apps/marble-maps/MarbleMaps.qrc @@ -1,79 +1,80 @@ 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 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 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 material-icons/ic_directions_bike_48px.svg material-icons/ic_directions_car_48px.svg material-icons/ic_directions_walk_48px.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 material-icons/ic_directions_bus_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 diff --git a/src/apps/marble-maps/Search.qml b/src/apps/marble-maps/Search.qml index 4c8dca13f..47ae6d54d 100644 --- a/src/apps/marble-maps/Search.qml +++ b/src/apps/marble-maps/Search.qml @@ -1,189 +1,191 @@ // // 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 // import QtQuick 2.3 import QtQuick.Controls 2.0 import QtQuick.Window 2.2 import org.kde.marble 0.20 Item { id: root property var marbleQuickItem: null signal itemSelected(var suggestedPlacemark) + signal menuButtonClicked() readonly property alias searchResultPlacemark: backend.selectedPlacemark readonly property alias searchResultsVisible: searchResults.visible onVisibleChanged: { if( !visible ) { searchResults.visible = false; searchField.query = ""; } } SystemPalette { id: palette colorGroup: SystemPalette.Active } SystemPalette { id: paletteDisabled colorGroup: SystemPalette.Disabled } SearchResults { id: searchResults anchors { top: searchField.bottom left: searchField.left } width: searchField.width height: delegateHeight * Math.min(10,count) visible: false onItemSelected: { backend.setSelectedPlacemark(index); root.itemSelected(backend.selectedPlacemark); searchResults.visible = false; } } Rectangle { id: background visible: searchField.hasFocus && searchField.query === "" anchors.top: searchField.bottom anchors.left: searchField.left width: searchField.width height: childrenRect.height + 2 * itemSpacing color: palette.base property int delegateHeight: 0 property double itemSpacing: Screen.pixelDensity * 1 Column { anchors.top: parent.top anchors.topMargin: background.itemSpacing anchors.left: parent.left anchors.right: parent.right anchors.margins: background.itemSpacing spacing: background.itemSpacing ListView { id: bookmarksView anchors.left: parent.left anchors.right: parent.right height: background.delegateHeight * Math.min(6, model.count) clip: true ScrollIndicator.vertical: ScrollIndicator { } model: bookmarks.model delegate: Row { width: bookmarksView.width height: background.itemSpacing + Math.max(bookmarkIcon.height, bookmarkText.height) spacing: background.itemSpacing Image { id: bookmarkIcon anchors.verticalCenter: parent.verticalCenter source: iconPath.substr(0,1) === '/' ? "file://" + iconPath : iconPath width: Screen.pixelDensity * 4 height: width sourceSize.width: width sourceSize.height: height } Text { id: bookmarkText anchors.verticalCenter: parent.verticalCenter anchors.leftMargin: Screen.pixelDensity * 2 width: bookmarksView.width - bookmarksView.spacing - bookmarkIcon.width text: display font.pointSize: 18 color: palette.text elide: Text.ElideMiddle MouseArea { anchors.fill: parent onClicked: { bookmarksView.currentIndex = index app.selectedPlacemark = bookmarks.placemark(index); itemSelected(bookmarks.placemark(index)) marbleMaps.centerOn(selectedPlacemark.longitude, selectedPlacemark.latitude) dialogLoader.focus = true } } } onHeightChanged: { if( background.delegateHeight !== height ) { background.delegateHeight = height; } } } } Row { visible: bookmarksView.model.count === 0 width: parent.width Text { anchors.bottom: parent.bottom 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.bottom: parent.bottom width: 0.2 * parent.width fillMode: Image.PreserveAspectFit source: "qrc:/konqi/books.png" } } } } SearchBackend { id: backend marbleQuickItem: root.marbleQuickItem onSearchResultChanged: { searchResults.model = model; searchResults.visible = true; } onSearchFinished: searchField.busy = false } SearchField { id: searchField width: parent.width - 2 * anchors.margins <= Screen.pixelDensity * 70 ? parent.width - 2 * anchors.margins : Screen.pixelDensity * 50 anchors { top: parent.top left: parent.left margins: Screen.pixelDensity * 3 } completionModel: backend.completionModel onSearchRequested: backend.search(query) onCompletionRequested: backend.setCompletionPrefix(query) onCleared: searchResults.visible = false + onMenuButtonClicked: root.menuButtonClicked() } Bookmarks { id: bookmarks map: root.marbleQuickItem } } diff --git a/src/apps/marble-maps/SearchField.qml b/src/apps/marble-maps/SearchField.qml index 3f088cb66..7921b3fda 100644 --- a/src/apps/marble-maps/SearchField.qml +++ b/src/apps/marble-maps/SearchField.qml @@ -1,143 +1,159 @@ // // 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 1.3 import QtQuick.Controls.Styles 1.3 Item { id: root height: field.height property alias query: field.text property alias hasFocus: field.activeFocus property alias completionModel: completion.model property bool busy: false signal searchRequested(string query) signal completionRequested(string query) signal cleared() + signal menuButtonClicked() function search(query) { routingManager.clearSearchResultPlacemarks(); query = query.trim(); if(query.toLowerCase() === "ok marble" || query.toLowerCase() === "okdbg") { app.state = "developer"; } else if(query !== "") { root.busy = true; searchRequested(query); field.focus = false; } } SystemPalette{ id: palette colorGroup: SystemPalette.Active } Rectangle { anchors.fill: parent color: palette.base border.color: palette.shadow border.width: 1 } + FlatButton { + id: menuButton + anchors.verticalCenter: parent.verticalCenter + anchors.left: parent.left + anchors.leftMargin: 10 + height: 0.7 * field.height + width: height + imageSource: "qrc:///menu.png" + + MouseArea { + anchors.fill: parent + onClicked: root.menuButtonClicked() + } + } + TextField { id: field - anchors.left: parent.left + anchors.left: menuButton.right anchors.right: parent.right placeholderText: qsTr("Search") font.pointSize: 18 textColor: palette.text inputMethodHints: Qt.ImhNoPredictiveText onAccepted: root.search(text) onTextChanged: root.completionRequested(text) BusyIndicator { id: searchBusyIndicator anchors.verticalCenter: parent.verticalCenter anchors.right: clearButton.visible ? clearButton.left : clearButton.right anchors.rightMargin: 10 visible: running height: 0.7 * field.height width: height running: root.busy style: BusyIndicatorStyle { indicator: Image { visible: control.running source: "busy_indicator.png" RotationAnimator on rotation { running: control.running loops: Animation.Infinite duration: 1500 from: 0 ; to: 360 } } } } FlatButton { id: clearButton anchors.verticalCenter: parent.verticalCenter anchors.right: searchButton.visible ? searchButton.left : parent.right anchors.rightMargin: 10 height: 0.7 * field.height width: height visible: field.text !== "" imageSource: "qrc:///clear.png" MouseArea { anchors.fill: parent onClicked: { app.selectedPlacemark = null; app.state = "none" routingManager.clearSearchResultPlacemarks(); field.text = ""; field.focus = true; cleared(); } } } FlatButton { id: searchButton anchors.verticalCenter: parent.verticalCenter anchors.right: parent.right anchors.rightMargin: 10 height: 0.7 * field.height width: height visible: !root.busy enabled: field.text !== "" imageSource: "qrc:///search.png" onClicked: root.search(field.text) } } Completion { id: completion anchors { top: parent.bottom left: parent.left right: parent.right } height: delegateHeight * Math.min(2,count) visible: count > 0 && field.activeFocus onItemSelected: { field.text = name; search(name); } } }