schermate di configurazione complete: razze disponibili, mio pollaio, registro galline, clienti, backup/ripristino nel formato dell'app Android, lingua a runtime, introduzione e informazioni
- datastore: export/import JSON con le chiavi camelCase dell'app Android (breedSettings/reservations/customers), scorte e ultimo aggiornamento preservati al ripristino - settings: flag 'introduzione vista' - desktop: permesso Documents (dove finisce il backup) - nuovi controlli: tools/check_qml.py (chiavi di traduzione, simboli JS, pagine, componenti) e qmllint nella suite - versione 0.2.0
This commit is contained in:
@@ -0,0 +1,65 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/i18n.js" as I18N
|
||||
|
||||
// Informazioni: descrizione dell'app e versione (dall'applicationVersion
|
||||
// impostata nel main.cpp, la stessa del pacchetto RPM).
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string lang: "en"
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
Component.onCompleted: page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
|
||||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: about.height
|
||||
|
||||
Column {
|
||||
id: about
|
||||
width: parent.width
|
||||
spacing: Theme.paddingLarge
|
||||
|
||||
PageHeader { title: page.tr("about") }
|
||||
|
||||
Image {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
width: Theme.itemSizeLarge * 2
|
||||
height: Theme.itemSizeLarge * 2
|
||||
source: "file:///usr/share/icons/hicolor/172x172/apps/harbour-henflow.png"
|
||||
fillMode: Image.PreserveAspectFit
|
||||
asynchronous: true
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "Henflow"
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.highlightColor
|
||||
}
|
||||
|
||||
Label {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
text: page.tr("about_desc")
|
||||
color: Theme.primaryColor
|
||||
wrapMode: Text.Wrap
|
||||
horizontalAlignment: Text.AlignHCenter
|
||||
}
|
||||
|
||||
Label {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: "v " + appVersion
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: Theme.paddingLarge }
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/i18n.js" as I18N
|
||||
|
||||
// Backup e ripristino, nello STESSO formato JSON dell'app Android
|
||||
// ({breedSettings, reservations, customers}): i file esportati dalle due
|
||||
// versioni si scambiano senza conversioni.
|
||||
// Qui non esiste la condivisione di Android: l'esportazione scrive il file
|
||||
// nella cartella Documenti (e lo si puo' copiare negli appunti), il
|
||||
// ripristino si fa incollando il JSON.
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string lang: "en"
|
||||
property var onChanged: null
|
||||
property string message: ""
|
||||
property bool messageIsError: false
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
function showMessage(text, isError) {
|
||||
page.message = text
|
||||
page.messageIsError = isError
|
||||
hideTimer.restart()
|
||||
}
|
||||
|
||||
function exportData() {
|
||||
var path = appData.exportBackup()
|
||||
if (path.length > 0)
|
||||
page.showMessage(page.tr("export_data") + ": " + path, false)
|
||||
else
|
||||
page.showMessage(page.tr("import_error"), true)
|
||||
}
|
||||
|
||||
function copyData() {
|
||||
clipboard.text = appData.backupJson()
|
||||
page.showMessage(page.tr("copied"), false)
|
||||
}
|
||||
|
||||
function importData() {
|
||||
if (!appData.isValidBackup(backupField.text)) {
|
||||
page.showMessage(page.tr("import_error"), true)
|
||||
return
|
||||
}
|
||||
if (appData.importBackup(backupField.text)) {
|
||||
backupField.text = ""
|
||||
page.showMessage(page.tr("import_success"), false)
|
||||
if (page.onChanged)
|
||||
page.onChanged()
|
||||
} else {
|
||||
page.showMessage(page.tr("import_error"), true)
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
|
||||
Timer {
|
||||
id: hideTimer
|
||||
interval: 6000
|
||||
onTriggered: page.message = ""
|
||||
}
|
||||
|
||||
Clipboard { id: clipboard }
|
||||
|
||||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: content.height
|
||||
|
||||
Column {
|
||||
id: content
|
||||
width: parent.width
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
PageHeader { title: page.tr("backup_restore") }
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: page.tr("export_data")
|
||||
onClicked: page.exportData()
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: page.tr("copy_to_clipboard")
|
||||
onClicked: page.copyData()
|
||||
}
|
||||
|
||||
Label {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
text: page.message
|
||||
color: page.messageIsError ? Theme.errorColor : Theme.highlightColor
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
wrapMode: Text.Wrap
|
||||
visible: text.length > 0
|
||||
}
|
||||
|
||||
SectionHeader { text: page.tr("import_data") }
|
||||
|
||||
TextArea {
|
||||
id: backupField
|
||||
width: parent.width
|
||||
height: Theme.itemSizeLarge * 3
|
||||
placeholderText: page.tr("paste_json_here")
|
||||
label: page.tr("paste_json_here")
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: page.tr("import_data")
|
||||
enabled: appData.isValidBackup(backupField.text)
|
||||
onClicked: page.importData()
|
||||
}
|
||||
|
||||
Label {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
text: appData.dataFilePath()
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: Theme.paddingLarge }
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,257 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/henflow.js" as HF
|
||||
import "../js/i18n.js" as I18N
|
||||
import "../js/catalog.js" as Catalog
|
||||
|
||||
// Scheda di una razza allevata: foto, scheda informativa e registro delle
|
||||
// galline (nome, nascita, eta' in mesi, stato) con interruttore deposizione
|
||||
// ed eliminazione con annulla.
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string breedId: ""
|
||||
property string lang: "en"
|
||||
property bool ready: false
|
||||
property var hens: []
|
||||
|
||||
property string breedName: ""
|
||||
property string breedDescription: ""
|
||||
property string eggColor: ""
|
||||
property int registered: 0
|
||||
property int laying: 0
|
||||
property real daily: 0
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
ListModel { id: henModel }
|
||||
|
||||
function reload() {
|
||||
page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
|
||||
var breed = Catalog.breedFromId(page.breedId)
|
||||
if (breed) {
|
||||
page.breedName = page.tr(breed.name)
|
||||
page.breedDescription = page.tr(breed.description)
|
||||
page.eggColor = page.tr("egg_color") + ": " + page.tr(breed.eggColor)
|
||||
}
|
||||
|
||||
var setting = null
|
||||
var settings = appData.breedSettings()
|
||||
for (var i = 0; i < settings.length; i++) {
|
||||
if (settings[i].breedId === page.breedId)
|
||||
setting = settings[i]
|
||||
}
|
||||
|
||||
page.hens = (setting && setting.hens) ? setting.hens : []
|
||||
page.registered = page.hens.length
|
||||
page.laying = HF.activeHens({ "hens": page.hens })
|
||||
page.daily = page.laying * HF.eggsPerHenPerDay(Catalog.HF_BREEDS, page.breedId)
|
||||
|
||||
var today = HF.todayMidnight()
|
||||
henList.clear()
|
||||
for (var j = 0; j < page.hens.length; j++) {
|
||||
var hen = page.hens[j]
|
||||
var birth = HF.parseIso(hen.birthDate)
|
||||
var months = birth ? HF.ageInMonths(birth, today) : 0
|
||||
var status
|
||||
if (months >= 6)
|
||||
status = hen.isLaying ? page.tr("status_laying") : page.tr("status_rest")
|
||||
else
|
||||
status = hen.isLaying ? page.tr("status_laying") : page.tr("status_young")
|
||||
|
||||
henList.append({
|
||||
"n": j,
|
||||
"name": hen.name,
|
||||
"birth": page.tr("birth") + ": " + HF.formatDay(hen.birthDate),
|
||||
"age": page.tr("age_months", months),
|
||||
"status": status,
|
||||
"isLaying": hen.isLaying
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Salva il registro delle galline di questa razza.
|
||||
function saveHens(list) {
|
||||
var settings = appData.breedSettings()
|
||||
for (var i = 0; i < settings.length; i++) {
|
||||
if (settings[i].breedId === page.breedId)
|
||||
settings[i].hens = list
|
||||
}
|
||||
appData.saveBreedSettings(settings)
|
||||
page.reload()
|
||||
}
|
||||
|
||||
function toggleHen(index) {
|
||||
var list = page.hens.slice()
|
||||
list[index].isLaying = !list[index].isLaying
|
||||
page.saveHens(list)
|
||||
}
|
||||
|
||||
function deleteHen(index) {
|
||||
var list = page.hens.slice()
|
||||
var hen = list[index]
|
||||
remorse.execute(henList, page.tr("delete"), function () {
|
||||
list.splice(index, 1)
|
||||
page.saveHens(list)
|
||||
}, 4000)
|
||||
}
|
||||
|
||||
function openHen(index) {
|
||||
var hen = (index >= 0 && index < page.hens.length) ? page.hens[index] : null
|
||||
pageStack.push(Qt.resolvedUrl("HenPage.qml"),
|
||||
{ "breedId": page.breedId, "hen": hen })
|
||||
}
|
||||
|
||||
Component.onCompleted: page.reload()
|
||||
|
||||
onStatusChanged: {
|
||||
if (status !== PageStatus.Active)
|
||||
return
|
||||
if (page.ready)
|
||||
page.reload()
|
||||
page.ready = true
|
||||
}
|
||||
|
||||
RemorseItem { id: remorse }
|
||||
|
||||
SilicaListView {
|
||||
id: henList
|
||||
anchors.fill: parent
|
||||
|
||||
header: Column {
|
||||
width: henList.width
|
||||
|
||||
PageHeader { title: page.tr("breed_sheet", page.breedName) }
|
||||
|
||||
Image {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: henList.width - 2 * Theme.horizontalPageMargin
|
||||
height: width / 2
|
||||
source: Catalog.breedFromId(page.breedId)
|
||||
? "file://" + Catalog.breedFromId(page.breedId).asset : ""
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
asynchronous: true
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: Theme.paddingMedium }
|
||||
|
||||
Column {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: henList.width - 2 * Theme.horizontalPageMargin
|
||||
spacing: Theme.paddingSmall
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: page.breedDescription
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: page.eggColor
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: page.tr("reg_hens") + ": " + page.registered
|
||||
+ " · " + page.tr("laying_6_months") + ": " + page.laying
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: page.tr("est_prod") + ": " + page.daily.toFixed(1) + " " + page.tr("eggs_per_day")
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: Theme.paddingMedium }
|
||||
|
||||
SectionHeader { text: page.tr("hen_registry") }
|
||||
}
|
||||
|
||||
model: henModel
|
||||
|
||||
delegate: ListItem {
|
||||
id: henItem
|
||||
contentHeight: henText.height + Theme.paddingMedium
|
||||
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
text: page.tr("delete")
|
||||
onClicked: page.deleteHen(index)
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: henText
|
||||
x: Theme.horizontalPageMargin
|
||||
y: Theme.paddingMedium
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin - layingSwitch.width
|
||||
- Theme.paddingMedium
|
||||
spacing: Theme.paddingSmall
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.name
|
||||
color: henItem.highlighted ? Theme.highlightColor : Theme.primaryColor
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.birth
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.age + " · " + model.status
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: layingSwitch
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: Theme.horizontalPageMargin
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
checked: model.isLaying
|
||||
onClicked: page.toggleHen(index)
|
||||
}
|
||||
|
||||
onClicked: page.openHen(index)
|
||||
}
|
||||
|
||||
ViewPlaceholder {
|
||||
enabled: henModel.count === 0
|
||||
text: page.tr("no_hens_reg")
|
||||
}
|
||||
|
||||
PullDownMenu {
|
||||
MenuItem {
|
||||
text: page.tr("new_hen")
|
||||
onClicked: page.openHen(-1)
|
||||
}
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,206 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/i18n.js" as I18N
|
||||
import "../js/catalog.js" as Catalog
|
||||
|
||||
// "Razze disponibili" (prima scheda del SettingsPage dell'app Flutter):
|
||||
// il catalogo completo con l'interruttore di attivazione. Attivare una razza
|
||||
// crea l'allevamento (senza galline), disattivarla lo elimina dopo conferma.
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string lang: "en"
|
||||
property bool ready: false
|
||||
// razze attive (id) ricostruite dal livello dati
|
||||
property var enabledIds: []
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
ListModel { id: breedModel }
|
||||
|
||||
function isEnabled(breedId) {
|
||||
return page.enabledIds.indexOf(breedId) >= 0
|
||||
}
|
||||
|
||||
function henCount(breedId) {
|
||||
var settings = appData.breedSettings()
|
||||
for (var i = 0; i < settings.length; i++) {
|
||||
if (settings[i].breedId === breedId) {
|
||||
var hens = settings[i].hens
|
||||
return hens ? hens.length : 0
|
||||
}
|
||||
}
|
||||
return 0
|
||||
}
|
||||
|
||||
function reload() {
|
||||
page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
|
||||
var settings = appData.breedSettings()
|
||||
var ids = []
|
||||
for (var i = 0; i < settings.length; i++)
|
||||
ids.push(settings[i].breedId)
|
||||
page.enabledIds = ids
|
||||
|
||||
breedModel.clear()
|
||||
for (var j = 0; j < Catalog.HF_BREEDS.length; j++) {
|
||||
var b = Catalog.HF_BREEDS[j]
|
||||
breedModel.append({
|
||||
"n": j,
|
||||
"name": page.tr(b.name),
|
||||
"description": page.tr(b.description),
|
||||
"eggColor": page.tr("egg_color") + ": " + page.tr(b.eggColor),
|
||||
"production": b.eggsPerHenPerDay.toFixed(1) + " " + page.tr("eggs_per_g"),
|
||||
"enabled": page.isEnabled(b.id),
|
||||
"hens": page.henCount(b.id)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Attivazione immediata; disattivazione con conferma (come l'originale,
|
||||
// che avverte perche' si perdono le galline registrate).
|
||||
function toggleBreed(index) {
|
||||
var breed = Catalog.HF_BREEDS[index]
|
||||
if (!breed)
|
||||
return
|
||||
if (page.isEnabled(breed.id)) {
|
||||
confirmDialog.pendingId = breed.id
|
||||
confirmDialog.open()
|
||||
} else {
|
||||
var settings = appData.breedSettings()
|
||||
settings.push({ "breedId": breed.id, "hens": [] })
|
||||
appData.saveBreedSettings(settings)
|
||||
page.reload()
|
||||
}
|
||||
}
|
||||
|
||||
function removeBreed(breedId) {
|
||||
var settings = appData.breedSettings()
|
||||
var out = []
|
||||
for (var i = 0; i < settings.length; i++) {
|
||||
if (settings[i].breedId !== breedId)
|
||||
out.push(settings[i])
|
||||
}
|
||||
appData.saveBreedSettings(out)
|
||||
page.reload()
|
||||
}
|
||||
|
||||
Component.onCompleted: page.reload()
|
||||
|
||||
onStatusChanged: {
|
||||
if (status !== PageStatus.Active)
|
||||
return
|
||||
if (page.ready)
|
||||
page.reload()
|
||||
page.ready = true
|
||||
}
|
||||
|
||||
SilicaListView {
|
||||
id: listView
|
||||
anchors.fill: parent
|
||||
|
||||
header: Column {
|
||||
width: listView.width
|
||||
|
||||
PageHeader { title: page.tr("available_breeds") }
|
||||
}
|
||||
|
||||
model: breedModel
|
||||
|
||||
delegate: ListItem {
|
||||
id: breedItem
|
||||
contentHeight: photo.height + Theme.paddingMedium
|
||||
|
||||
Image {
|
||||
id: photo
|
||||
x: Theme.horizontalPageMargin
|
||||
y: Theme.paddingMedium
|
||||
width: Theme.itemSizeLarge
|
||||
height: Theme.itemSizeLarge
|
||||
source: "file://" + Catalog.HF_BREEDS[model.n].asset
|
||||
fillMode: Image.PreserveAspectCrop
|
||||
asynchronous: true
|
||||
}
|
||||
|
||||
Column {
|
||||
x: photo.x + photo.width + Theme.paddingMedium
|
||||
y: Theme.paddingMedium
|
||||
width: parent.width - x - switchControl.width - 2 * Theme.horizontalPageMargin
|
||||
spacing: Theme.paddingSmall
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.name
|
||||
color: breedItem.highlighted ? Theme.highlightColor : Theme.primaryColor
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.description
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
wrapMode: Text.Wrap
|
||||
maximumLineCount: 2
|
||||
truncationMode: TruncationMode.Elide
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.eggColor
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.hens > 0 ? (model.production + " · " + model.hens + " " + page.tr("hens"))
|
||||
: model.production
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
}
|
||||
|
||||
Switch {
|
||||
id: switchControl
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: Theme.horizontalPageMargin
|
||||
verticalCenter: photo.verticalCenter
|
||||
}
|
||||
checked: model.enabled
|
||||
onClicked: page.toggleBreed(model.n)
|
||||
}
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
|
||||
Dialog {
|
||||
id: confirmDialog
|
||||
property string pendingId: ""
|
||||
|
||||
Column {
|
||||
width: parent.width
|
||||
|
||||
DialogHeader {
|
||||
title: page.tr("confirm_deactivation")
|
||||
acceptText: page.tr("deactivate")
|
||||
}
|
||||
|
||||
Label {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
text: page.tr("delete_msg")
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
|
||||
onAccepted: page.removeBreed(confirmDialog.pendingId)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/henflow.js" as HF
|
||||
import "../js/i18n.js" as I18N
|
||||
import "../js/catalog.js" as Catalog
|
||||
|
||||
// "Mio pollaio" (seconda scheda dell'originale): produzione del giorno e
|
||||
// razze allevate, con conteggi e stima per razza. Il tocco apre il registro
|
||||
// delle galline di quella razza.
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string lang: "en"
|
||||
property bool ready: false
|
||||
property var settings: []
|
||||
property real totalProduction: 0
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
ListModel { id: breedModel }
|
||||
|
||||
function reload() {
|
||||
page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
page.settings = appData.breedSettings()
|
||||
page.totalProduction = HF.totalDailyProduction(page.settings, Catalog.HF_BREEDS)
|
||||
|
||||
breedModel.clear()
|
||||
for (var i = 0; i < page.settings.length; i++) {
|
||||
var s = page.settings[i]
|
||||
var breed = Catalog.breedFromId(s.breedId)
|
||||
var laying = HF.activeHens(s)
|
||||
var daily = laying * HF.eggsPerHenPerDay(Catalog.HF_BREEDS, s.breedId)
|
||||
breedModel.append({
|
||||
"n": i,
|
||||
"name": breed ? page.tr(breed.name) : s.breedId,
|
||||
"registered": page.tr("reg_hens") + ": " + (s.hens ? s.hens.length : 0),
|
||||
"laying": page.tr("laying_6_months") + ": " + laying,
|
||||
"production": page.tr("est_prod") + ": " + daily.toFixed(1) + " " + page.tr("eggs_per_day")
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: page.reload()
|
||||
|
||||
onStatusChanged: {
|
||||
if (status !== PageStatus.Active)
|
||||
return
|
||||
if (page.ready)
|
||||
page.reload()
|
||||
page.ready = true
|
||||
}
|
||||
|
||||
SilicaListView {
|
||||
id: listView
|
||||
anchors.fill: parent
|
||||
|
||||
header: Column {
|
||||
width: listView.width
|
||||
|
||||
PageHeader { title: page.tr("my_coop") }
|
||||
|
||||
Rectangle {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: listView.width - 2 * Theme.horizontalPageMargin
|
||||
height: coopColumn.height + 2 * Theme.paddingMedium
|
||||
radius: Theme.paddingSmall
|
||||
color: Theme.rgba(Theme.highlightBackgroundColor, 0.12)
|
||||
|
||||
Column {
|
||||
id: coopColumn
|
||||
x: Theme.paddingMedium
|
||||
y: Theme.paddingMedium
|
||||
width: parent.width - 2 * Theme.paddingMedium
|
||||
spacing: Theme.paddingSmall
|
||||
|
||||
Label {
|
||||
text: page.tr("daily_prod_est")
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
color: Theme.secondaryColor
|
||||
}
|
||||
|
||||
Label {
|
||||
text: page.totalProduction.toFixed(1) + " " + page.tr("eggs_per_day")
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.highlightColor
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: Theme.paddingMedium }
|
||||
|
||||
SectionHeader { text: page.tr("available_breeds") }
|
||||
}
|
||||
|
||||
model: breedModel
|
||||
|
||||
delegate: ListItem {
|
||||
id: coopItem
|
||||
contentHeight: coopText.height + Theme.paddingMedium
|
||||
|
||||
Column {
|
||||
id: coopText
|
||||
x: Theme.horizontalPageMargin
|
||||
y: Theme.paddingMedium
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
spacing: Theme.paddingTiny
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.name
|
||||
color: coopItem.highlighted ? Theme.highlightColor : Theme.primaryColor
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.registered + " · " + model.laying
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.production
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("BreedDetailPage.qml"),
|
||||
{ "breedId": page.settings[index].breedId })
|
||||
}
|
||||
|
||||
ViewPlaceholder {
|
||||
enabled: breedModel.count === 0
|
||||
text: page.tr("no_breeds")
|
||||
hintText: page.tr("config_breed_first")
|
||||
}
|
||||
|
||||
PullDownMenu {
|
||||
MenuItem {
|
||||
text: page.tr("available_breeds")
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("BreedsPage.qml"))
|
||||
}
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/henflow.js" as HF
|
||||
import "../js/i18n.js" as I18N
|
||||
|
||||
// Nuovo cliente / modifica: nome di battesimo (obbligatorio), cognome e
|
||||
// telefono. Stessa regola dell'app Flutter: senza nome non si salva.
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property var customer: null // null = nuovo cliente
|
||||
property var onSaved: null
|
||||
property string lang: "en"
|
||||
property string errorMsg: ""
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
function reload() {
|
||||
page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
if (page.customer) {
|
||||
firstNameField.text = page.customer.firstName || ""
|
||||
lastNameField.text = page.customer.lastName || ""
|
||||
phoneField.text = page.customer.phoneNumber || ""
|
||||
}
|
||||
}
|
||||
|
||||
function save() {
|
||||
var first = firstNameField.text.trim()
|
||||
if (first.length === 0) {
|
||||
page.errorMsg = page.tr("first_name")
|
||||
return
|
||||
}
|
||||
|
||||
var list = appData.customers().slice()
|
||||
var record = {
|
||||
"id": page.customer ? page.customer.id : HF.newId(),
|
||||
"firstName": first,
|
||||
"lastName": lastNameField.text.trim(),
|
||||
"phoneNumber": phoneField.text.trim()
|
||||
}
|
||||
|
||||
if (page.customer) {
|
||||
for (var i = 0; i < list.length; i++) {
|
||||
if (list[i].id === record.id)
|
||||
list[i] = record
|
||||
}
|
||||
} else {
|
||||
list.push(record)
|
||||
}
|
||||
|
||||
appData.saveCustomers(list)
|
||||
if (page.onSaved)
|
||||
page.onSaved()
|
||||
pageStack.pop()
|
||||
}
|
||||
|
||||
Component.onCompleted: page.reload()
|
||||
|
||||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: form.height
|
||||
|
||||
Column {
|
||||
id: form
|
||||
width: parent.width
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
PageHeader {
|
||||
title: page.customer ? page.tr("edit_customer") : page.tr("new_customer")
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: firstNameField
|
||||
width: parent.width
|
||||
label: page.tr("first_name")
|
||||
EnterKey.enabled: true
|
||||
EnterKey.onClicked: page.save()
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: lastNameField
|
||||
width: parent.width
|
||||
label: page.tr("last_name")
|
||||
EnterKey.enabled: true
|
||||
EnterKey.onClicked: page.save()
|
||||
}
|
||||
|
||||
TextField {
|
||||
id: phoneField
|
||||
width: parent.width
|
||||
label: page.tr("phone_number")
|
||||
inputMethodHints: Qt.ImhDialableCharactersOnly
|
||||
EnterKey.enabled: true
|
||||
EnterKey.onClicked: page.save()
|
||||
}
|
||||
|
||||
Label {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
text: page.errorMsg
|
||||
color: Theme.errorColor
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
wrapMode: Text.Wrap
|
||||
visible: text.length > 0
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: page.tr("save")
|
||||
onClicked: page.save()
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: Theme.paddingLarge }
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/i18n.js" as I18N
|
||||
|
||||
// Clienti abituali: nome, cognome e telefono. Servono a velocizzare
|
||||
// l'inserimento degli ordini (i suggerimenti sono nella scheda ordine).
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string lang: "en"
|
||||
property bool ready: false
|
||||
property var customers: []
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
ListModel { id: customerModel }
|
||||
|
||||
function fullName(c) {
|
||||
var n = (c.firstName || "") + " " + (c.lastName || "")
|
||||
return n.trim()
|
||||
}
|
||||
|
||||
function reload() {
|
||||
page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
page.customers = appData.customers()
|
||||
|
||||
customerModel.clear()
|
||||
for (var i = 0; i < page.customers.length; i++) {
|
||||
customerModel.append({
|
||||
"n": i,
|
||||
"name": page.fullName(page.customers[i]),
|
||||
"phone": page.customers[i].phoneNumber || ""
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function openCustomer(index) {
|
||||
var customer = (index >= 0 && index < page.customers.length) ? page.customers[index] : null
|
||||
pageStack.push(Qt.resolvedUrl("CustomerPage.qml"),
|
||||
{ "customer": customer, "onSaved": function () { page.reload() } })
|
||||
}
|
||||
|
||||
function deleteCustomer(index) {
|
||||
var list = appData.customers().slice()
|
||||
remorse.execute(listView, page.tr("delete"), function () {
|
||||
list.splice(index, 1)
|
||||
appData.saveCustomers(list)
|
||||
page.reload()
|
||||
}, 4000)
|
||||
}
|
||||
|
||||
Component.onCompleted: page.reload()
|
||||
|
||||
onStatusChanged: {
|
||||
if (status !== PageStatus.Active)
|
||||
return
|
||||
if (page.ready)
|
||||
page.reload()
|
||||
page.ready = true
|
||||
}
|
||||
|
||||
RemorseItem { id: remorse }
|
||||
|
||||
SilicaListView {
|
||||
id: listView
|
||||
anchors.fill: parent
|
||||
|
||||
header: PageHeader { title: page.tr("customers_list") }
|
||||
model: customerModel
|
||||
|
||||
delegate: ListItem {
|
||||
id: customerItem
|
||||
contentHeight: customerText.height + Theme.paddingMedium
|
||||
|
||||
menu: ContextMenu {
|
||||
MenuItem {
|
||||
text: page.tr("edit_customer")
|
||||
onClicked: page.openCustomer(index)
|
||||
}
|
||||
MenuItem {
|
||||
text: page.tr("delete")
|
||||
onClicked: page.deleteCustomer(index)
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
id: customerText
|
||||
x: Theme.horizontalPageMargin
|
||||
y: Theme.paddingMedium
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
spacing: Theme.paddingSmall
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.name
|
||||
color: customerItem.highlighted ? Theme.highlightColor : Theme.primaryColor
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: model.phone
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeExtraSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
visible: text.length > 0
|
||||
}
|
||||
}
|
||||
|
||||
onClicked: page.openCustomer(index)
|
||||
}
|
||||
|
||||
ViewPlaceholder {
|
||||
enabled: customerModel.count === 0
|
||||
text: page.tr("no_customers")
|
||||
}
|
||||
|
||||
PullDownMenu {
|
||||
MenuItem {
|
||||
text: page.tr("new_customer")
|
||||
onClicked: page.openCustomer(-1)
|
||||
}
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,261 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/henflow.js" as HF
|
||||
import "../js/i18n.js" as I18N
|
||||
import "../js/catalog.js" as Catalog
|
||||
|
||||
// Nuova gallina / modifica di una gallina: nome, data di nascita e stato di
|
||||
// deposizione. La data si sceglie con tre selettori (giorno, mese, anno):
|
||||
// la combinazione viene mostrata e controllata subito (niente date impossibili).
|
||||
// Come nell'app Flutter, alla scelta della data lo stato si adegua da solo:
|
||||
// sotto i 6 mesi la gallina non e' in deposizione.
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string breedId: ""
|
||||
property var hen: null // null = nuova gallina
|
||||
property string lang: "en"
|
||||
|
||||
property int year: 0
|
||||
property int month: 1
|
||||
property int day: 1
|
||||
property int daysInMonth: 31
|
||||
property bool laying: true
|
||||
property string errorMsg: ""
|
||||
|
||||
readonly property int firstYear: 2010
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
function monthLength(y, m) {
|
||||
// giorni del mese: il 31 c'e' solo dove esiste davvero
|
||||
if (m === 2)
|
||||
return (y % 4 === 0 && (y % 100 !== 0 || y % 400 === 0)) ? 29 : 28
|
||||
if (m === 4 || m === 6 || m === 9 || m === 11)
|
||||
return 30
|
||||
return 31
|
||||
}
|
||||
|
||||
function isoOfSelection() {
|
||||
return HF.isoDay(new Date(page.year, page.month - 1, page.day))
|
||||
+ "T00:00:00.000"
|
||||
}
|
||||
|
||||
function monthsOfSelection() {
|
||||
return HF.ageInMonths(new Date(page.year, page.month - 1, page.day), HF.todayMidnight())
|
||||
}
|
||||
|
||||
function refreshSelection() {
|
||||
page.daysInMonth = page.monthLength(page.year, page.month)
|
||||
if (page.day > page.daysInMonth)
|
||||
page.day = page.daysInMonth
|
||||
}
|
||||
|
||||
function applyDefaultDate() {
|
||||
// Come l'originale: si parte da 180 giorni fa (una gallina in deposizione)
|
||||
var d = HF.addDays(HF.todayMidnight(), -180)
|
||||
page.year = d.getFullYear()
|
||||
page.month = d.getMonth() + 1
|
||||
page.day = d.getDate()
|
||||
page.refreshSelection()
|
||||
}
|
||||
|
||||
function reload() {
|
||||
page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
|
||||
if (page.hen) {
|
||||
var birth = HF.parseIso(page.hen.birthDate)
|
||||
if (birth) {
|
||||
page.year = birth.getFullYear()
|
||||
page.month = birth.getMonth() + 1
|
||||
page.day = birth.getDate()
|
||||
} else {
|
||||
page.applyDefaultDate()
|
||||
}
|
||||
page.laying = page.hen.isLaying
|
||||
nameField.text = page.hen.name
|
||||
} else {
|
||||
page.applyDefaultDate()
|
||||
page.laying = page.monthsOfSelection() >= 6
|
||||
}
|
||||
page.refreshSelection()
|
||||
}
|
||||
|
||||
function save() {
|
||||
var name = nameField.text.trim()
|
||||
if (name.length === 0) {
|
||||
page.errorMsg = page.tr("hen_name")
|
||||
return
|
||||
}
|
||||
|
||||
var settings = appData.breedSettings()
|
||||
var target = null
|
||||
for (var i = 0; i < settings.length; i++) {
|
||||
if (settings[i].breedId === page.breedId)
|
||||
target = settings[i]
|
||||
}
|
||||
if (!target) {
|
||||
page.errorMsg = page.tr("config_breed_first")
|
||||
return
|
||||
}
|
||||
if (!target.hens)
|
||||
target.hens = []
|
||||
|
||||
var record = {
|
||||
"id": page.hen ? page.hen.id : HF.newId(),
|
||||
"name": name,
|
||||
"birthDate": page.isoOfSelection(),
|
||||
"isLaying": page.laying
|
||||
}
|
||||
|
||||
if (page.hen) {
|
||||
for (var j = 0; j < target.hens.length; j++) {
|
||||
if (target.hens[j].id === record.id)
|
||||
target.hens[j] = record
|
||||
}
|
||||
} else {
|
||||
target.hens.push(record)
|
||||
}
|
||||
|
||||
appData.saveBreedSettings(settings)
|
||||
pageStack.pop()
|
||||
}
|
||||
|
||||
Component.onCompleted: page.reload()
|
||||
|
||||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: form.height
|
||||
|
||||
Column {
|
||||
id: form
|
||||
width: parent.width
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
PageHeader { title: page.tr("new_hen") }
|
||||
|
||||
TextField {
|
||||
id: nameField
|
||||
width: parent.width
|
||||
label: page.tr("hen_name")
|
||||
placeholderText: page.tr("hen_name")
|
||||
EnterKey.enabled: true
|
||||
EnterKey.onClicked: page.save()
|
||||
}
|
||||
|
||||
SectionHeader { text: page.tr("birth_date") }
|
||||
|
||||
Row {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
spacing: Theme.paddingSmall
|
||||
|
||||
ComboBox {
|
||||
id: dayBox
|
||||
width: (parent.width - 2 * parent.spacing) / 3
|
||||
label: "DD"
|
||||
menu: ContextMenu {
|
||||
Repeater {
|
||||
model: page.daysInMonth
|
||||
delegate: MenuItem {
|
||||
text: String(index + 1)
|
||||
onClicked: {
|
||||
page.day = index + 1
|
||||
dayBox.currentIndex = index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: monthBox
|
||||
width: (parent.width - 2 * parent.spacing) / 3
|
||||
label: "MM"
|
||||
menu: ContextMenu {
|
||||
Repeater {
|
||||
model: 12
|
||||
delegate: MenuItem {
|
||||
text: String(index + 1)
|
||||
onClicked: {
|
||||
page.month = index + 1
|
||||
page.refreshSelection()
|
||||
monthBox.currentIndex = index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ComboBox {
|
||||
id: yearBox
|
||||
width: (parent.width - 2 * parent.spacing) / 3
|
||||
label: "YYYY"
|
||||
menu: ContextMenu {
|
||||
Repeater {
|
||||
model: HF.todayMidnight().getFullYear() - page.firstYear + 1
|
||||
delegate: MenuItem {
|
||||
text: String(page.firstYear + index)
|
||||
onClicked: {
|
||||
page.year = page.firstYear + index
|
||||
page.refreshSelection()
|
||||
yearBox.currentIndex = index
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Column {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
spacing: Theme.paddingSmall
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: page.tr("birth") + ": " + HF.formatDay(page.isoOfSelection())
|
||||
color: Theme.primaryColor
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: page.tr("age_months", page.monthsOfSelection())
|
||||
+ " · " + (page.monthsOfSelection() >= 6 ? page.tr("laying_6_months") : page.tr("status_young"))
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
}
|
||||
|
||||
TextSwitch {
|
||||
id: layingSwitch
|
||||
text: page.tr("in_laying")
|
||||
checked: page.laying
|
||||
onClicked: page.laying = !page.laying
|
||||
}
|
||||
|
||||
Label {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
text: page.errorMsg
|
||||
color: Theme.errorColor
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
wrapMode: Text.Wrap
|
||||
visible: text.length > 0
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: page.tr("save")
|
||||
onClicked: page.save()
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: Theme.paddingLarge }
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/i18n.js" as I18N
|
||||
|
||||
// Introduzione: i quattro passi dell'app Flutter (registro galline, uova,
|
||||
// clienti, ordini). Si apre da sola al primo avvio e resta raggiungibile
|
||||
// dalle impostazioni; in quel caso il pulsante finale torna indietro.
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property bool firstRun: true
|
||||
property string lang: "en"
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
function finish() {
|
||||
if (page.firstRun)
|
||||
appSettings.introSeen = true
|
||||
pageStack.pop()
|
||||
}
|
||||
|
||||
Component.onCompleted: page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
|
||||
SilicaFlickable {
|
||||
anchors.fill: parent
|
||||
contentHeight: intro.height
|
||||
|
||||
Column {
|
||||
id: intro
|
||||
width: parent.width
|
||||
spacing: Theme.paddingMedium
|
||||
|
||||
PageHeader { title: page.tr("welcome") }
|
||||
|
||||
Repeater {
|
||||
model: 4
|
||||
delegate: Column {
|
||||
x: Theme.horizontalPageMargin
|
||||
width: intro.width - 2 * Theme.horizontalPageMargin
|
||||
spacing: Theme.paddingSmall
|
||||
|
||||
SectionHeader {
|
||||
text: index === 0 ? page.tr("intro_step1_title")
|
||||
: index === 1 ? page.tr("intro_step2_title")
|
||||
: index === 2 ? page.tr("intro_step3_title")
|
||||
: page.tr("intro_step4_title")
|
||||
}
|
||||
|
||||
Label {
|
||||
width: parent.width
|
||||
text: index === 0 ? page.tr("intro_step1_desc")
|
||||
: index === 1 ? page.tr("intro_step2_desc")
|
||||
: index === 2 ? page.tr("intro_step3_desc")
|
||||
: page.tr("intro_step4_desc")
|
||||
color: Theme.secondaryColor
|
||||
font.pixelSize: Theme.fontSizeSmall
|
||||
wrapMode: Text.Wrap
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
anchors.horizontalCenter: parent.horizontalCenter
|
||||
text: page.tr("start_app")
|
||||
onClicked: page.finish()
|
||||
}
|
||||
|
||||
Item { width: parent.width; height: Theme.paddingLarge }
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,81 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/i18n.js" as I18N
|
||||
|
||||
// Lingua dell'applicazione: le quattro dell'originale (EN/IT/FR/DE).
|
||||
// A differenza di QTranslator, la tabella di traduzione in i18n.js si applica
|
||||
// subito: le pagine che si riaprono usano la lingua nuova senza riavviare.
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string lang: "en"
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
ListModel { id: langModel }
|
||||
|
||||
readonly property var codes: [ "en", "it", "fr", "de" ]
|
||||
readonly property var names: [ "English", "Italiano", "Français", "Deutsch" ]
|
||||
|
||||
function reload() {
|
||||
page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
langModel.clear()
|
||||
for (var i = 0; i < page.codes.length; i++) {
|
||||
langModel.append({
|
||||
"n": i,
|
||||
"name": page.names[i],
|
||||
"current": page.codes[i] === page.lang
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
function choose(index) {
|
||||
var code = page.codes[index]
|
||||
if (appSettings.language === code) {
|
||||
pageStack.pop()
|
||||
return
|
||||
}
|
||||
appSettings.language = code
|
||||
page.reload()
|
||||
}
|
||||
|
||||
Component.onCompleted: page.reload()
|
||||
|
||||
SilicaListView {
|
||||
id: listView
|
||||
anchors.fill: parent
|
||||
|
||||
header: PageHeader { title: page.tr("language") }
|
||||
model: langModel
|
||||
|
||||
delegate: ListItem {
|
||||
id: langItem
|
||||
|
||||
Label {
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: Theme.horizontalPageMargin
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
text: model.name
|
||||
color: langItem.highlighted ? Theme.highlightColor
|
||||
: (model.current ? Theme.highlightColor : Theme.primaryColor)
|
||||
}
|
||||
|
||||
Icon {
|
||||
anchors {
|
||||
right: parent.right
|
||||
rightMargin: Theme.horizontalPageMargin
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
source: "image://theme/icon-m-accept"
|
||||
visible: model.current
|
||||
}
|
||||
|
||||
onClicked: page.choose(index)
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
}
|
||||
+33
-2
@@ -36,7 +36,6 @@ Page {
|
||||
page.totalProduction = HF.totalDailyProduction(page.breeds, Catalog.HF_BREEDS)
|
||||
page.productionRows = HF.productionDetail(page.breeds, Catalog.HF_BREEDS)
|
||||
page.rebuildList()
|
||||
page.ready = true
|
||||
}
|
||||
|
||||
// Accrual del magazzino all'apertura (come _loadData in main.dart): i giorni
|
||||
@@ -278,6 +277,14 @@ Page {
|
||||
text: page.tr("new_order")
|
||||
onClicked: page.openOrder(-1)
|
||||
}
|
||||
MenuItem {
|
||||
text: page.tr("customers_list")
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("CustomersPage.qml"))
|
||||
}
|
||||
MenuItem {
|
||||
text: page.tr("settings")
|
||||
onClicked: pageStack.push(Qt.resolvedUrl("SettingsPage.qml"))
|
||||
}
|
||||
}
|
||||
|
||||
footer: Item {
|
||||
@@ -325,5 +332,29 @@ Page {
|
||||
}
|
||||
}
|
||||
|
||||
Component.onCompleted: page.reload()
|
||||
Component.onCompleted: {
|
||||
page.reload()
|
||||
|
||||
// Prima apertura: l'introduzione si presenta da sola (come l'originale).
|
||||
// Differita: spingere una pagina mentre la prima si sta creando e'
|
||||
// una corsa che su Qt 5.6 fa fallire l'apertura.
|
||||
if (!appSettings.introSeen)
|
||||
introTimer.start()
|
||||
}
|
||||
|
||||
// Al ritorno dalle altre pagine i dati possono essere cambiati (galline,
|
||||
// ordini, clienti, ripristino di un backup): la Home si ricarica.
|
||||
onStatusChanged: {
|
||||
if (status !== PageStatus.Active)
|
||||
return
|
||||
if (page.ready)
|
||||
page.reload()
|
||||
page.ready = true
|
||||
}
|
||||
|
||||
Timer {
|
||||
id: introTimer
|
||||
interval: 100
|
||||
onTriggered: pageStack.push(Qt.resolvedUrl("IntroPage.qml"), { "firstRun": true })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -250,7 +250,7 @@ Page {
|
||||
id: previewLabel
|
||||
x: Theme.horizontalPageMargin
|
||||
width: parent.width - 2 * Theme.horizontalPageMargin
|
||||
font.pixelSize: Theme.fontSizeMedium
|
||||
font.pixelSize: Theme.fontSizeLarge
|
||||
color: Theme.highlightColor
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
import QtQuick 2.2
|
||||
import Sailfish.Silica 1.0
|
||||
import "../js/i18n.js" as I18N
|
||||
|
||||
// Impostazioni: razze disponibili, mio pollaio, clienti, backup, lingua,
|
||||
// introduzione, informazioni. Le voci dell'app Flutter erano tre schede piu'
|
||||
// il selettore di lingua nella barra: qui diventano una lista di voci.
|
||||
Page {
|
||||
id: page
|
||||
allowedOrientations: Orientation.All
|
||||
|
||||
property string lang: "en"
|
||||
// Avvisa la Home che i dati sono cambiati (usato dal ripristino).
|
||||
property var onChanged: null
|
||||
property bool ready: false
|
||||
|
||||
function tr(key, arg) { return I18N.t(key, arg, page.lang) }
|
||||
|
||||
ListModel { id: entries }
|
||||
|
||||
function reload() {
|
||||
page.lang = I18N.langFor(appSettings.language, Qt.locale().name)
|
||||
entries.clear()
|
||||
entries.append({ "label": page.tr("available_breeds") })
|
||||
entries.append({ "label": page.tr("my_coop") })
|
||||
entries.append({ "label": page.tr("customers_list") })
|
||||
entries.append({ "label": page.tr("backup_restore") })
|
||||
entries.append({ "label": page.tr("language") })
|
||||
entries.append({ "label": page.tr("show_intro") })
|
||||
entries.append({ "label": page.tr("about") })
|
||||
}
|
||||
|
||||
function openEntry(index) {
|
||||
if (index === 0) {
|
||||
pageStack.push(Qt.resolvedUrl("BreedsPage.qml"))
|
||||
} else if (index === 1) {
|
||||
pageStack.push(Qt.resolvedUrl("CoopPage.qml"))
|
||||
} else if (index === 2) {
|
||||
pageStack.push(Qt.resolvedUrl("CustomersPage.qml"))
|
||||
} else if (index === 3) {
|
||||
pageStack.push(Qt.resolvedUrl("BackupPage.qml"),
|
||||
{ "onChanged": page.forwardChanged })
|
||||
} else if (index === 4) {
|
||||
pageStack.push(Qt.resolvedUrl("LanguagePage.qml"))
|
||||
} else if (index === 5) {
|
||||
pageStack.push(Qt.resolvedUrl("IntroPage.qml"), { "firstRun": false })
|
||||
} else if (index === 6) {
|
||||
pageStack.push(Qt.resolvedUrl("AboutPage.qml"))
|
||||
}
|
||||
}
|
||||
|
||||
function forwardChanged() {
|
||||
if (page.onChanged)
|
||||
page.onChanged()
|
||||
}
|
||||
|
||||
Component.onCompleted: page.reload()
|
||||
|
||||
// al ritorno da una sottopagina i dati possono essere cambiati
|
||||
onStatusChanged: {
|
||||
if (status !== PageStatus.Active)
|
||||
return
|
||||
if (page.ready)
|
||||
page.reload()
|
||||
page.ready = true
|
||||
}
|
||||
|
||||
SilicaListView {
|
||||
id: listView
|
||||
anchors.fill: parent
|
||||
|
||||
header: PageHeader { title: page.tr("settings") }
|
||||
model: entries
|
||||
|
||||
delegate: ListItem {
|
||||
id: entry
|
||||
|
||||
Label {
|
||||
anchors {
|
||||
left: parent.left
|
||||
leftMargin: Theme.horizontalPageMargin
|
||||
right: parent.right
|
||||
rightMargin: Theme.horizontalPageMargin
|
||||
verticalCenter: parent.verticalCenter
|
||||
}
|
||||
text: model.label
|
||||
color: entry.highlighted ? Theme.highlightColor : Theme.primaryColor
|
||||
truncationMode: TruncationMode.Fade
|
||||
}
|
||||
|
||||
onClicked: page.openEntry(index)
|
||||
}
|
||||
|
||||
VerticalScrollDecorator {}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user