Files
harbour-tide/qml/pages/MainPage.qml
T

219 lines
7.5 KiB
QML

import QtQuick 2.0
import Sailfish.Silica 1.0
import "../js/tides.js" as Tides
Page {
id: page
property var customPorts: [] // porti creati dalla ricerca libera (sessione)
// Campi di ricerca FUORI dal header della lista: gli id dichiarati nel
// componente header hanno scope chiuso e le funzioni del Page non li
// vedono (ReferenceError "filter is not defined").
Column {
id: topCol
width: parent.width
PageHeader { title: "Tide" }
SearchField {
id: filter
placeholderText: qsTr("Filter ports…")
onTextChanged: rebuild()
}
Item { width: parent.width; height: Theme.paddingMedium }
TextField {
id: geoField
width: parent.width - 2 * Theme.horizontalPageMargin
anchors.horizontalCenter: parent.horizontalCenter
placeholderText: qsTr("Free search: any city (online)…")
EnterKey.enabled: text.length > 0
EnterKey.onClicked: findCity()
}
Button {
text: qsTr("Find")
anchors.horizontalCenter: parent.horizontalCenter
onClicked: findCity()
}
Label {
id: geoMsg
visible: false
width: parent.width - 2 * Theme.horizontalPageMargin
anchors.horizontalCenter: parent.horizontalCenter
wrapMode: Text.Wrap
color: Theme.errorColor
font.pixelSize: Theme.fontSizeSmall
}
}
SilicaListView {
id: list
anchors.top: topCol.bottom
anchors.left: parent.left
anchors.right: parent.right
anchors.bottom: parent.bottom
model: ListModel { id: portModel }
header: SectionHeader { text: qsTr("Ports") }
delegate: ListItem {
contentHeight: Theme.itemSizeMedium
highlighted: model.isPreferred
Label {
text: model.name
color: model.isPreferred ? Theme.highlightColor : Theme.primaryColor
font.pixelSize: Theme.fontSizeMedium
anchors.left: parent.left
anchors.leftMargin: Theme.horizontalPageMargin
anchors.verticalCenter: parent.verticalCenter
truncationMode: TruncationMode.Fade
width: parent.width - 3 * Theme.horizontalPageMargin
}
Label {
text: model.country
color: Theme.secondaryColor
font.pixelSize: Theme.fontSizeSmall
anchors.left: parent.left
anchors.leftMargin: Theme.horizontalPageMargin
anchors.top: parent.top
anchors.topMargin: Theme.paddingSmall
}
Label {
text: model.range
color: Theme.secondaryColor
font.pixelSize: Theme.fontSizeSmall
anchors.right: parent.right
anchors.rightMargin: Theme.horizontalPageMargin
anchors.verticalCenter: parent.verticalCenter
}
onClicked: {
page.pageStack.push(Qt.resolvedUrl("DetailPage.qml"),
{ port: model.port, portIndex: model.realIndex, custom: model.custom })
}
}
VerticalScrollDecorator {}
}
function rebuild() {
portModel.clear()
var f = filter.text.toLowerCase()
var pref = appSettings.preferredPort
var i, p
for (i = 0; i < Tides.PORTS.length; i++) {
p = Tides.PORTS[i]
if (f !== "" && (p[0] + " " + p[1]).toLowerCase().indexOf(f) === -1)
continue
portModel.append({
name: p[0], country: p[1],
range: "±" + (p[4][1][0] + p[4][2][0]).toFixed(2) + " m",
port: p, realIndex: i, custom: false,
isPreferred: (pref === p[0])
})
}
for (i = 0; i < customPorts.length; i++) {
p = customPorts[i]
if (f !== "" && p[0].toLowerCase().indexOf(f) === -1)
continue
portModel.append({
name: p[0], country: qsTr("searched city"),
range: "±" + (p[4][1][0] + p[4][2][0]).toFixed(2) + " m",
port: p, realIndex: -1, custom: true,
isPreferred: (pref === p[0])
})
}
}
// --- Ricerca libera (geocoding) ---
function geoFail() {
geoMsg.text = qsTr("City not found (offline and not in local dataset).")
geoMsg.visible = true
geoMsg.color = Theme.errorColor
}
function geoFound(name, lat, lon, src) {
if (!name) { geoFail(); return }
var np = Tides.nearestPort(lat, lon)
var cost, flag
if (np.km < 222) {
cost = np.port[4].slice()
flag = "near:" + np.port[0] + ":" + (np.km / 111).toFixed(1)
} else {
cost = Tides.synthConst(lat, lon)
flag = "synth"
}
var port = [name, "", lat, lon, cost, flag]
customPorts.push(port)
rebuild()
geoMsg.text = qsTr("Found %1 (via %2)").arg(name).arg(src)
geoMsg.visible = true
geoMsg.color = Theme.highlightColor
page.pageStack.push(Qt.resolvedUrl("DetailPage.qml"),
{ port: port, portIndex: -1, custom: true })
}
function geoXhr(query, cb) {
// Photon (komoot) invece di Nominatim: accetta qualsiasi User-Agent
// (Nominatim risponde 403 all'UA generico inviato dalle XHR QML di Qt)
// e supporta il filtro osm_tag=place:city per le città vere.
var xhr = new XMLHttpRequest()
xhr.open("GET", "https://photon.komoot.io/api/?limit=1&osm_tag=place:city&q="
+ encodeURIComponent(query), true)
xhr.onreadystatechange = function() {
if (xhr.readyState === 4) {
if (xhr.status === 200) {
try {
var j = JSON.parse(xhr.responseText)
if (j && j.features && j.features.length > 0) {
var f = j.features[0]
var name = f.properties.name || query
cb(name,
f.geometry.coordinates[1],
f.geometry.coordinates[0],
"OpenStreetMap")
} else {
cb(null)
}
} catch (e) { cb(null) }
} else {
cb(null)
}
}
}
xhr.send()
}
function findCity() {
var q = geoField.text.trim()
if (q === "") return
geoMsg.text = qsTr("Searching…")
geoMsg.visible = true
geoMsg.color = Theme.secondaryColor
var loc = Tides.findCityLocal(q)
if (loc) {
geoFound(loc[0], loc[2], loc[3], qsTr("local dataset"))
} else {
geoXhr(q, geoFound)
}
}
Component.onCompleted: {
rebuild()
var pref = appSettings.preferredPort
if (pref !== "") {
for (var i = 0; i < Tides.PORTS.length; i++) {
if (Tides.PORTS[i][0] === pref) {
page.pageStack.push(Qt.resolvedUrl("DetailPage.qml"),
{ port: Tides.PORTS[i], portIndex: i, custom: false })
break
}
}
}
}
}