diff --git a/src/qml/common/InformationTable.qml b/src/qml/common/InformationTable.qml index c4aadc1..408c540 100644 --- a/src/qml/common/InformationTable.qml +++ b/src/qml/common/InformationTable.qml @@ -1,65 +1,73 @@ /* * Copyright 2012 Sebastian Gottfried * Copyright 2015 Sebastian Gottfried * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.9 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import ktouch 1.0 Item { property alias model: repeator.model id: root height: childrenRect.height + KColorScheme { + id: colorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.View + } + + Column { id: column spacing: 3 width: parent.width height: childrenRect.height Repeater { id: repeator Row { spacing: 5 height: Math.max(titleLabel.height, valueLabel.height) width: root.width Label { id: titleLabel width: Math.round((parent.width - parent.spacing) / 2) horizontalAlignment: Text.AlignRight verticalAlignment: Text.AlignTop color: "#888" text: model.modelData.title wrapMode: Text.Wrap height: Math.max(paintedHeight, valueLabel.paintedHeight) } Label { id: valueLabel width: parent.width - titleLabel.width - parent.spacing horizontalAlignment: Text.AlignLeft verticalAlignment: Text.AlignBottom text: model.modelData.text wrapMode: Text.Wrap height: Math.max(paintedHeight, titleLabel.paintedHeight) + color: colorScheme.normalText } } } } } diff --git a/src/qml/homescreen/ProfileDetailsItem.qml b/src/qml/homescreen/ProfileDetailsItem.qml index 45975a6..e0bc046 100644 --- a/src/qml/homescreen/ProfileDetailsItem.qml +++ b/src/qml/homescreen/ProfileDetailsItem.qml @@ -1,277 +1,284 @@ /* * Copyright 2012 Sebastian Gottfried * Copyright 2015 Sebastian Gottfried * Copyright 2017 Sebastian Gottfried * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.9 import QtQuick.Layouts 1.3 import org.kde.charts 0.1 as Charts import org.kde.kcoreaddons 1.0 import ktouch 1.0 import "../common" Item { id: root property Profile profile function update() { if (profile) { var isNewProfile = root.profile.id === -1 profileForm.name = profile.name profileForm.skillLevel = profile.skillLevel profileForm.skillLevelSelectionEnabled = isNewProfile deleteConfirmationLabel.name = profile.name state = isNewProfile? "editor": "info" } } signal deletionRequest(); onProfileChanged: update() SystemPalette { id: activePalette colorGroup: SystemPalette.Active } + KColorScheme { + id: colorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.View + } + Item { id: infoContainer width: parent.width height: childrenRect.height anchors.centerIn: parent Column { width: parent.width height: childrenRect.height spacing: 40 LearningProgressModel { id: learningProgressModel profile: root.profile } Rectangle { anchors.horizontalCenter: parent.horizontalCenter width: parent.width height: 250 color: activePalette.base border { width: 1 color: activePalette.text } Column { id: column anchors { fill: parent topMargin: column.spacing + spacing + legend.height leftMargin: column.spacing rightMargin: column.spacing bottomMargin: column.spacing } spacing: 20 width: parent.width height: parent.height - legend.height - parent.spacing LearningProgressChart { id: learningProgressChart anchors.horizontalCenter: parent.horizontalCenter width: parent.width height: parent.height - legend.height - parent.spacing - + backgroundColor: colorScheme.normalBackground + textColor: colorScheme.normalText model: learningProgressModel } Row { id: legend anchors.horizontalCenter: parent.horizontalCenter spacing: 20 Charts.LegendItem { dimension: learningProgressChart.accuracy - textColor: activePalette.text + textColor: colorScheme.normalText } Charts.LegendItem { dimension: learningProgressChart.charactersPerMinute - textColor: activePalette.text + textColor: colorScheme.normalText } } } } InformationTable { id: profileInfoTable width: parent.width property int trainedLessonCount: profile && profile.id !== -1? profileDataAccess.lessonsTrained(profile): 0 property list infoModel: [ InfoItem { title: i18n("Lessons trained:") text: profile && profile.id !== -1? profileInfoTable.trainedLessonCount: "" }, InfoItem { title: i18n("Total training time:") text: profile && profile.id !== -1? Format.formatDuration(profileDataAccess.totalTrainingTime(profile)): "" }, InfoItem { title: i18n("Last trained:") text: profile && profile.id !== -1 && profileInfoTable.trainedLessonCount > 0? profileDataAccess.lastTrainingSession(profile).toLocaleDateString(): i18n("Never") } ] model: infoModel } } InlineToolbar { anchors { top: parent.top horizontalCenter: parent.horizontalCenter topMargin: 5 } content: [ IconToolButton { iconName: "document-edit" text: i18n("Edit") onClicked: root.state = "editor" }, IconToolButton { iconName: "edit-delete" text: i18n("Delete") enabled: profileDataAccess.profileCount > 1 onClicked: root.state = "deleteConfirmation" } ] } } Item { id: editorContainer width: parent.width height: childrenRect.height anchors.centerIn: parent ProfileForm { id: profileForm width: parent.width showWelcomeLabel: false onDone: { root.profile.name = profileForm.name root.profile.skillLevel = profileForm.skillLevel if (root.profile.id === -1) { profileDataAccess.addProfile(profile) } else { profileDataAccess.updateProfile(profileDataAccess.indexOfProfile(root.profile)) } root.update() root.state = "info" } } } Item { id: deleteConfirmationContainer width: parent.width height: childrenRect.height anchors.centerIn: parent Column { width: parent.width height: childrenRect.height spacing: 15 Label { property string name id: deleteConfirmationLabel width: parent.width text: i18n("Do you really want to delete the profile \"%1\"?", name) wrapMode: Text.Wrap horizontalAlignment: Text.AlignHCenter } Row { spacing: 10 anchors.horizontalCenter: parent.horizontalCenter width: childrenRect.width height: childrenRect.height IconButton { iconName: "edit-delete" text: i18n("Delete") bgColor: colorScheme.negativeBackground onClicked: root.deletionRequest() } IconButton { text: i18n("Cancel") onClicked: root.state = "info" } } } } states: [ State { name: "info" PropertyChanges { target: infoContainer visible: true } PropertyChanges { target: editorContainer visible: false } PropertyChanges { target: deleteConfirmationContainer visible: false } }, State { name: "editor" PropertyChanges { target: infoContainer visible: false } PropertyChanges { target: editorContainer visible: true } PropertyChanges { target: deleteConfirmationContainer visible: false } }, State { name: "deleteConfirmation" PropertyChanges { target: infoContainer visible: false } PropertyChanges { target: editorContainer visible: false } PropertyChanges { target: deleteConfirmationContainer visible: true } } ] } diff --git a/src/qml/homescreen/StatPopupDialog.qml b/src/qml/homescreen/StatPopupDialog.qml index 63dbf0f..d0a2ac6 100644 --- a/src/qml/homescreen/StatPopupDialog.qml +++ b/src/qml/homescreen/StatPopupDialog.qml @@ -1,90 +1,93 @@ /* * Copyright 2017 Sebastian Gottfried * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.9 import QtQuick.Layouts 1.3 import ktouch 1.0 import org.kde.charts 0.1 as Charts import '../common' PopupDialog { id: root property alias course: learningProgressModel.courseFilter property alias lesson: learningProgressModel.lessonFilter property alias profile: learningProgressModel.profile title: i18n("Lesson training statistics") modal: true margins: { left: 40 bottom: 40 right: 40 top: 40 } width: parent.width - leftMargin - rightMargin height: parent.height - topMargin - bottomMargin padding: 0 contentItem: Rectangle { color: colorScheme.normalBackground ColumnLayout { anchors.fill: parent anchors.margins: titleLabel.font.pixelSize spacing: titleLabel.font.pixelSize Label { id: titleLabel text: lesson? lesson.title: "" font.bold: true } LearningProgressChart { id: learningProgressChart Layout.fillHeight: true Layout.fillWidth: true backgroundColor: colorScheme.normalBackground + textColor: colorScheme.normalText model: LearningProgressModel { id: learningProgressModel } } Row { spacing: 2 * titleLabel.font.pixelSize Layout.alignment: Qt.AlignTop | Qt.AlignHCenter Charts.LegendItem { id: accuracyLegend dimension: learningProgressChart.accuracy + textColor: colorScheme.normalText } Charts.LegendItem { id: charactersPerMinuteLegend dimension: learningProgressChart.charactersPerMinute + textColor: colorScheme.normalText } } } KColorScheme { id: colorScheme colorGroup: KColorScheme.Active colorSet: KColorScheme.View } } } diff --git a/src/qml/scorescreen/ScoreScreen.qml b/src/qml/scorescreen/ScoreScreen.qml index f2e3f67..e912bec 100644 --- a/src/qml/scorescreen/ScoreScreen.qml +++ b/src/qml/scorescreen/ScoreScreen.qml @@ -1,419 +1,419 @@ /* * Copyright 2018 Sebastian Gottfried * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License, or (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ import QtQuick 2.9 import QtQuick.Layouts 1.3 import ktouch 1.0 import org.kde.charts 0.1 as Charts import "../common" import "../meters" FocusScope { id: screen function start() {} function reset() { internal.lessonPassed = Math.round(1000 * stats.accuracy) >= Math.round(10 * preferences.requiredAccuracy) && stats.charactersPerMinute >= preferences.requiredStrokesPerMinute var lessonIndex = 0; for (var i = 0; i < course.lessonCount; i++) { if (lesson === course.lesson(i)) { lessonIndex = i break } } var lastUnlockedLessonIndex = 0 if (profile.skillLevel === Profile.Advanced) { lastUnlockedLessonIndex = course.lessonCount - 1; internal.nextLessonUnlocked = false } else { var lastUnlockedLessonId = profileDataAccess.courseProgress(profile, course.id, ProfileDataAccess.LastUnlockedLesson); if (lastUnlockedLessonId !== "") { for (var index = 0; index < course.lessonCount; index++) { if (course.lesson(index).id === lastUnlockedLessonId) { lastUnlockedLessonIndex = index; break; } } } internal.nextLessonUnlocked = internal.lessonPassed && lessonIndex === lastUnlockedLessonIndex && lessonIndex + 1 < course.lessonCount if (internal.nextLessonUnlocked) { lastUnlockedLessonIndex++ } } internal.nextLesson = null; if (lessonIndex + 1 < course.lessonCount && lessonIndex + 1 <= lastUnlockedLessonIndex) { internal.nextLesson = course.lesson(lessonIndex + 1) } if (internal.nextLessonUnlocked) { profileDataAccess.saveCourseProgress(internal.nextLesson.id, profile, course.id, ProfileDataAccess.LastUnlockedLesson) } learningProgressModel.update() } function forceActiveFocus() { if (internal.lessonPassed && internal.nextLesson) { nextLessonButton.forceActiveFocus() } else { repeatLessonButton.forceActiveFocus() } } property Profile profile property Lesson lesson property Course course property TrainingStats stats property TrainingStats referenceStats signal homeScreenRequested signal nextLessonRequested(variant lesson) signal lessonRepetionRequested QtObject { id: internal property bool lessonPassed: false property bool nextLessonUnlocked: false property Lesson nextLesson: null } KColorScheme { id: colorScheme colorGroup: KColorScheme.Active colorSet: KColorScheme.View } - LearningProgressModel { property bool filterByLesson: learningProgressFilterComboBox.currentIndex == 1 id: learningProgressModel profile: screen.visible? screen.profile: null courseFilter: screen.visible? screen.course: null lessonFilter: screen.visible && filterByLesson? screen.lesson: null } ErrorsModel { id: errorsModel trainingStats: screen.visible? screen.stats: null } Balloon { id: errorsTooltip visualParent: parent property int row: -1 InformationTable { property list infoModel: [ InfoItem { title: i18n("Character:") text: errorsTooltip.row !== -1? errorsModel.character(errorsTooltip.row): "" }, InfoItem { title: i18n("Errors:") text: errorsTooltip.row !== -1? errorsModel.errors(errorsTooltip.row): "" } ] width: 250 model: infoModel } } ColumnLayout { anchors.fill: parent spacing: 0 ToolBar { id: header Layout.fillWidth: true RowLayout { anchors.fill: parent anchors.leftMargin: 20 anchors.rightMargin: 20 spacing: anchors.leftMargin IconToolButton { id: homeScreenButton text: i18n("Return to Home Screen") iconName: "go-home" colorScheme.colorSet: KColorScheme.Complementary onClicked: screen.homeScreenRequested() } Item { Layout.fillHeight: true Layout.fillWidth: true } IconToolButton { id: repeatLessonButton iconName: "view-refresh" text: i18n("Repeat Lesson") colorScheme.colorSet: KColorScheme.Complementary onClicked: screen.lessonRepetionRequested() } IconToolButton { id: nextLessonButton iconName: "go-next-view" text: i18n("Next Lesson") enabled: internal.nextLesson colorScheme.colorSet: KColorScheme.Complementary onClicked: screen.nextLessonRequested(internal.nextLesson) } } } Rectangle { Layout.fillHeight: true Layout.fillWidth: true color: colorScheme.normalBackground ColumnLayout { id: content anchors.fill: parent anchors.margins: 20 spacing: 15 Rectangle { id: caption Layout.fillWidth: true Layout.preferredHeight: captionContent.height + 30 radius: 15 color: "#eee4be" Item { id: captionContent anchors.centerIn: parent width: Math.max(mainCaption.width, subCaption.width) height: subCaption.visible? mainCaption.height + subCaption.height + 5: mainCaption.height Item { id: mainCaption anchors.top: parent.top anchors.horizontalCenter: parent.horizontalCenter width: captionIcon.width + captionLabel.width + 7 height: Math.max(captionIcon.height, captionLabel.height) Icon { id: captionIcon anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter icon: internal.lessonPassed? "dialog-ok-apply": "dialog-cancel" width: height height: captionLabel.height } Label { id: captionLabel anchors.right: parent.right anchors.verticalCenter: parent.verticalCenter text: internal.lessonPassed? i18n("Congratulations! You have passed the lesson."): i18n("You have not passed the lesson.") font.pointSize: 1.5 * Qt.font({'family': 'sansserif'}).pointSize color: "#000000" } } Label { id: subCaption anchors.bottom: parent.bottom anchors.horizontalCenter: parent.horizontalCenter visible: text !== "" text: { if (!internal.lessonPassed) return i18n("Repeat the lesson. Your skills will improve automatically over time.") if (internal.nextLessonUnlocked) return i18n("Next lesson unlocked") return "" } color: "#000000" } } } Rectangle { id: statsBox Layout.fillWidth: true Layout.preferredHeight: 140 radius: 15 color: "#cccccc" StatBox { anchors.centerIn: parent width: parent.width - 26 stats: screen.stats referenceStats: screen.referenceStats } } Item { id: contentSpacer Layout.preferredHeight: 10 Layout.fillWidth: true } RowLayout { id: chartControls spacing: 5 Layout.fillWidth: true Label { id: showLabel Layout.alignment: Qt.AlignVCenter text: i18nc("Show a specific type of statistic", "Show") opacity: 0.7 } ComboBox { id: chartTypeComboBox Layout.alignment: Qt.AlignVCenter model: ListModel { Component.onCompleted: { append({"text": i18n("Progress"), "icon": "office-chart-area"}); append({"text": i18n("Errors"), "icon": "office-chart-bar"}); } } textRole: "text" currentIndex: 0 } Label { id: overLabel Layout.alignment: Qt.AlignVCenter text: i18nc("Show a statistic over one or more lessons", "Over") opacity: tabGroup.currentItem === learningProgressTab? 0.7: 0 Behavior on opacity { NumberAnimation {duration: 150} } } ComboBox { id: learningProgressFilterComboBox Layout.alignment: Qt.AlignVCenter model: ListModel { Component.onCompleted: { append({"text": i18n("All Lessons"), "icon": "view-filter"}); append({"text": i18n("This Lesson"), "icon": "view-filter"}); } } textRole: "text" currentIndex: 0 opacity: tabGroup.currentItem === learningProgressTab? 1: 0 Behavior on opacity { NumberAnimation {duration: 150} } } Item { Layout.fillWidth: true } Charts.LegendItem { id: accuracyLegend textColor: colorScheme.normalText Layout.alignment: Qt.AlignVCenter opacity: tabGroup.currentItem === learningProgressTab? 1: 0 Behavior on opacity { NumberAnimation {duration: 150} } } Charts.LegendItem { id: charactersPerMinuteLegend textColor: colorScheme.normalText Layout.alignment: Qt.AlignVCenter opacity: tabGroup.currentItem === learningProgressTab? 1: 0 Behavior on opacity { NumberAnimation {duration: 150} } } } Item { Layout.fillHeight: true Layout.fillWidth: true StackLayout { anchors.fill: parent id: tabGroup currentIndex: chartTypeComboBox.currentIndex property Item currentItem: currentIndex != -1? children[currentIndex]: null LearningProgressChart { id: learningProgressTab property string title: i18n("Progress") property string iconName: "office-chart-area" model: learningProgressModel backgroundColor: colorScheme.normalBackground + textColor: colorScheme.normalText Component.onCompleted: { accuracyLegend.dimension = learningProgressTab.accuracy charactersPerMinuteLegend.dimension = learningProgressTab.charactersPerMinute } Component.onDestruction: { accuracyLegend.dimension = null charactersPerMinuteLegend.dimension = null } } Charts.BarChart{ id: errorsTab property string title: i18n("Errors") property string iconName: "office-chart-bar" model: errorsModel pitch: 60 textRole: 3 // Qt::ToolTipRole backgroundColor: colorScheme.normalBackground dimensions: [ Charts.Dimension { dataColumn: 0 color: "#ffb12d" maximumValue: Math.max(4, Math.ceil(errorsModel.maximumErrorCount / 4) * 4) label: i18n("Errors") } ] onElemEntered: { errorsTooltip.visualParent = elem; errorsTooltip.row = row errorsTooltip.open() } onElemExited: { errorsTooltip.close() } } } } } } } }