diff --git a/src/qml/homescreen/CourseSelector.qml b/src/qml/homescreen/CourseSelector.qml index 8d2008f..ac404bc 100644 --- a/src/qml/homescreen/CourseSelector.qml +++ b/src/qml/homescreen/CourseSelector.qml @@ -1,192 +1,196 @@ /* * 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.Layouts 1.3 import QtQuick.Controls 2.2 as Controls import ktouch 1.0 import "../common" FocusScope { id: root property Profile profile property string currentKeyboardLayoutName property string selectedKeyboardLayoutName property DataIndexKeyboardLayout selectedKeyboardLayout property DataIndexCourse selectedCourse signal lessonSelected(variant course, variant lesson) signal courseSelectec(Course course) function selectLastUsedCourse() { if (!profile) { return } var courseId = profile.lastUsedCourseId; // fist try to to select the course the user has used last for (var i = 0; i < allCoursesModel.rowCount(); i++) { var dataIndexCourse = allCoursesModel.data(allCoursesModel.index(i, 0), ResourceModel.DataRole); if (dataIndexCourse.id === courseId) { root.selectedCourse = dataIndexCourse return } } // if this fails try to select course matching the current keyboard layout if (coursesForCurrentKeyboardLayoutModel.rowCount() > 0) { var blub = coursesForCurrentKeyboardLayoutModel.data(coursesForCurrentKeyboardLayoutModel.index(0, 0), ResourceModel.DataRole); console.log(blub) root.selectedCourse = blub return; } // finally just select the first course if (allCoursesModel.rowCount() > 0) { root.selectedCourse = allCoursesModel.data(allCoursesModel.index(0, 0), ResourceModel.DataRole); } } onSelectedCourseChanged: { root.selectedKeyboardLayoutName = root.selectedCourse.keyboardLayoutName; for (var i = 0; i < ktouch.globalDataIndex.keyboardLayoutCount; i++) { var dataIndexLayout = ktouch.globalDataIndex.keyboardLayout(i) if (dataIndexLayout.name === root.selectedKeyboardLayoutName) { root.selectedKeyboardLayout = dataIndexLayout; return } } root.selectedKeyboardLayout = null; } function saveLastUsedCourse(course) { if (profile.lastUsedCourseId != course.id) { profile.lastUsedCourseId = course.id; profileDataAccess.updateProfile(profileDataAccess.indexOfProfile(profile)); } } onProfileChanged: selectLastUsedCourse() ResourceModel { - id: resourceModel + id: allResourcesModel dataIndex: ktouch.globalDataIndex onRowsRemoved: { selectLastUsedCourse() } onRowsInserted: { selectLastUsedCourse() } } CategorizedResourceSortFilterProxyModel { id: allCoursesModel - resourceModel: resourceModel + resourceModel: allResourcesModel resourceTypeFilter: ResourceModel.CourseItem } CategorizedResourceSortFilterProxyModel { id: coursesForCurrentKeyboardLayoutModel - resourceModel: resourceModel + resourceModel: allResourcesModel resourceTypeFilter: ResourceModel.CourseItem keyboardLayoutNameFilter: root.currentKeyboardLayoutName } CategorizedResourceSortFilterProxyModel { id: currentKeyboardLayoutsModel - resourceModel: resourceModel + resourceModel: allResourcesModel resourceTypeFilter: ResourceModel.KeyboardLayoutItem keyboardLayoutNameFilter: root.currentKeyboardLayoutName } CategorizedResourceSortFilterProxyModel { id: otherKeyboardLayoutsModel - resourceModel: resourceModel + resourceModel: allResourcesModel resourceTypeFilter: ResourceModel.KeyboardLayoutItem keyboardLayoutNameFilter: root.currentKeyboardLayoutName invertedKeyboardLayoutNameFilter: true } KColorScheme { id: courseSelectorColorScheme colorGroup: KColorScheme.Active colorSet: KColorScheme.View } Rectangle { id: bg anchors.fill: parent color: courseSelectorColorScheme.normalBackground } Flickable { clip: true anchors.fill: parent contentWidth: width contentHeight: content.height Column { id: content width: parent.width - CourseSelectorKeyboardLayoutList { + Loader { width: parent.width - title: i18n("Courses For Your Keyboard Layout") - model: currentKeyboardLayoutsModel - resourceModel: resourceModel - colorScheme: courseSelectorColorScheme - selectedKeyboardLayoutName: root.selectedKeyboardLayoutName - selectedCourse: root.selectedCourse - onCourseSelected: { - root.selectedCourse = course - root.saveLastUsedCourse(course) + active: root.currentKeyboardLayoutName != 'unknown' + sourceComponent: CourseSelectorKeyboardLayoutList { + width: parent.width + title: i18n("Courses For Your Keyboard Layout") + model: currentKeyboardLayoutsModel + resourceModel: allResourcesModel + colorScheme: courseSelectorColorScheme + selectedKeyboardLayoutName: root.selectedKeyboardLayoutName + selectedCourse: root.selectedCourse + onCourseSelected: { + root.selectedCourse = course + root.saveLastUsedCourse(course) + } } } CourseSelectorKeyboardLayoutList { width: parent.width - title: i18n("Other Courses") + title: root.currentKeyboardLayoutName == 'unknown'? i18n("Courses"): i18n("Other Courses") model: otherKeyboardLayoutsModel - resourceModel: resourceModel + resourceModel: allResourcesModel colorScheme: courseSelectorColorScheme selectedKeyboardLayoutName: root.selectedKeyboardLayoutName selectedCourse: root.selectedCourse onCourseSelected: { root.selectedCourse = course root.saveLastUsedCourse(course) } } } Controls.ScrollBar.vertical: ScrollBar { } } } diff --git a/src/qml/homescreen/KeyboardLayoutMismatchMessage.qml b/src/qml/homescreen/KeyboardLayoutMismatchMessage.qml index c375a14..16ca17d 100644 --- a/src/qml/homescreen/KeyboardLayoutMismatchMessage.qml +++ b/src/qml/homescreen/KeyboardLayoutMismatchMessage.qml @@ -1,81 +1,84 @@ /* * 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 "../common" Collapsable { id: root + property string activeKeyboardLayoutName: '' background: Rectangle { color: colorScheme.neutralBackground } Item { implicitWidth: root.width implicitHeight: layout.implicitHeight + 20 GridLayout { id: layout anchors.centerIn: parent width: root.width - 40 rowSpacing: label.font.pixelSize columnSpacing: 10 Icon { Layout.column: 0 Layout.row: 0 width: 32 height: 32 icon: "dialog-warning" } Label { id: label Layout.column: 1 Layout.row: 0 Layout.fillWidth: true font.bold: true wrapMode: Text.Wrap - text: i18n("The selected course doesn't match your computer's active keyboard layout.") + text: root.activeKeyboardLayoutName == 'unknown'? + i18n("The selected course is intended for a specific keyboard layout."): + i18n("The selected course is not intended for your computer's active keyboard layout.") } Label { Layout.column: 1 Layout.row: 1 Layout.columnSpan: configureKeyboardButton.visible? 2: 1 Layout.fillWidth: true wrapMode: Text.Wrap text: i18n("KTouch can't switch or set up keyboard layouts. Before training, you have to configure your computer to use the correct keyboard layout. Otherwise you will have to use your current keyboard layout to type the lesson text.") opacity: 0.7 } IconButton { id: configureKeyboardButton Layout.column: 2 Layout.row: 0 visible: ktouch.keyboardKCMAvailable text: i18n("Configure Keyboard") onClicked: { ktouch.showKeyboardKCM(); } } } } } diff --git a/src/qml/homescreen/LessonSelector.qml b/src/qml/homescreen/LessonSelector.qml index f7bf538..7c0a58d 100644 --- a/src/qml/homescreen/LessonSelector.qml +++ b/src/qml/homescreen/LessonSelector.qml @@ -1,391 +1,392 @@ /* * 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.Layouts 1.3 import ktouch 1.0 import QtGraphicalEffects 1.0 import "../common" FocusScope { id: root property Profile profile property DataIndexCourse dataIndexCourse property KeyboardLayout selectedKeyboardLayout property string activeKeyboardLayoutName property alias course: courseItem property Lesson selectedLesson: null signal lessonSelected(Course course, Lesson lesson) function update() { if (!course.isValid) return; if (!profile) return; course.updateLastUnlockedLessonIndex(); selectLastLesson() } function selectLastLesson() { var lessonId = profileDataAccess.courseProgress(profile, course.id, ProfileDataAccess.LastSelectedLesson); if (lessonId !== "") { for (var index = 0; index < course.lessonCount; index++) { if (course.lesson(index).id === lessonId) { root.selectedLesson = course.lesson(index) content.currentIndex = index return } } } if (course.lessonCount > 0) { root.selectedLesson = course.lesson(0) content.currentIndex = 0 } } onProfileChanged: update() onDataIndexCourseChanged: { root.selectedLesson = null; course.update(); root.update(); } Course { id: courseItem property int lastUnlockedLessonIndex: -1 property bool editable: courseItem.isValid && courseItem.id === "custom_lessons" function update() { if (root.dataIndexCourse === null) { return } if (dataIndexCourse.id == "custom_lessons") { profileDataAccess.loadCustomLessons(root.profile, dataIndexCourse.keyboardLayoutName, courseItem) } else { if (isValid && courseItem.id === dataIndexCourse.id) { return } dataAccess.loadCourse(dataIndexCourse, courseItem) } } function updateLastUnlockedLessonIndex() { lastUnlockedLessonIndex = 0; if (course.kind == Course.LessonCollection || profile.skillLevel === Profile.Advanced) { lastUnlockedLessonIndex = course.lessonCount - 1; return } var lastUnlockedLessonId = profileDataAccess.courseProgress(profile, course.id, ProfileDataAccess.LastUnlockedLesson); if (lastUnlockedLessonId !== "") { for (var index = 0; index < course.lessonCount; index++) { lastUnlockedLessonIndex = index if (course.lesson(index).id === lastUnlockedLessonId) { return } } } } function createNewCustomLesson() { var lesson = ktouch.createLesson(); lesson.id = utils.uuid() profileDataAccess.storeCustomLesson(lesson, root.profile, root.selectedKeyboardLayout.name) course.addLesson(lesson) updateLastUnlockedLessonIndex() content.currentIndex = course.indexOfLesson(lesson) root.selectedLesson = lesson lessonEditorDialog.open() } function deleteCustomLesson() { var msgItem = lessonDeletedMessageComponent.createObject(header); msgItem.deletedLesson.copyFrom(selectedLesson); profileDataAccess.deleteCustomLesson(selectedLesson.id); course.removeLesson(course.indexOfLesson(selectedLesson)); root.selectedLesson = null; content.currentIndex = -1; } function restoreCustomLesson(lesson) { course.addLesson(lesson); profileDataAccess.storeCustomLesson(lesson, root.profile, root.selectedKeyboardLayout.name) content.currentIndex = course.indexOfLesson(lesson); updateLastUnlockedLessonIndex(); } Component.onCompleted: update() } StatPopupDialog { id: statPopupDialog profile: root.profile course: courseItem lesson: root.selectedLesson } LessonEditorDialog { id: lessonEditorDialog profile: root.profile keyboardLayout: root.selectedKeyboardLayout lesson: root.selectedLesson } ColumnLayout { anchors.fill: parent spacing: 0 Item { Layout.fillWidth: true Layout.preferredHeight: header.height z: 2 Column { id: header width: parent.width ToolBar { id: toolbar width: parent.width dimFactor: 1.5 RowLayout { anchors.fill: parent anchors.leftMargin: 20 spacing: 5 Label { text: root.course? root.course.title: "" font.bold: true color: toolbar.colorScheme.normalText } IconToolButton { id: toggleCourseDesciptionButton iconName: "help-about" checkable: true color: toolbar.colorScheme.normalText backgroundColor: toolbar.colorScheme.normalBackground Layout.fillHeight: true Layout.preferredWidth: toolbar.height } ToolSeparator { visible: courseItem.editable } IconToolButton { id: newLessonButton iconName: "document-new" text: "Add New Lesson" color: toolbar.colorScheme.normalText backgroundColor: toolbar.colorScheme.normalBackground visible: courseItem.editable Layout.fillHeight: true onClicked: { course.createNewCustomLesson() } } Item { Layout.fillWidth: true } IconToolButton { id: configureButton iconName: "application-menu" color: toolbar.colorScheme.normalText backgroundColor: toolbar.colorScheme.normalBackground Layout.fillHeight: true Layout.preferredWidth: toolbar.height onClicked: { var position = mapToItem(null, 0, height) ktouch.showMenu(position.x, position.y) } } } } CourseDescriptionItem { id: courseDescriptionItem width: parent.width collapsed: !toggleCourseDesciptionButton.checked description: courseItem.description } KeyboardLayoutMismatchMessage { width: parent.width + activeKeyboardLayoutName: root.activeKeyboardLayoutName collapsed: !root.course || !root.course.isValid || root.activeKeyboardLayoutName == root.course.keyboardLayoutName } Component { id: lessonDeletedMessageComponent LessonDeletedMessage { width: parent.width onUndeleteRequested: { course.restoreCustomLesson(deletedLesson) } } } } DropShadow { anchors.fill: header source: header samples: 16 horizontalOffset: 0 verticalOffset: 0 } } Item { Layout.fillHeight: true Layout.fillWidth: true z: 1 Rectangle { anchors.fill: parent color: content.colorScheme.shade(content.colorScheme.normalBackground, KColorScheme.DarkShade, 1, 0.0) } GridView { id: content anchors.fill: parent clip: true focus: true background.color: colorScheme.shade(colorScheme.normalBackground, KColorScheme.DarkShade, 1, 0.0) property int columns: Math.floor(width / (300 + 20)) cellWidth: Math.floor(content.width / content.columns) cellHeight: Math.round(cellWidth * 2 / 3) Keys.onPressed: { if (event.key === Qt.Key_Return || event.key === Qt.Key_Enter) { event.accepted = true; if (root.selectedLesson && content.currentIndex <= course.lastUnlockedLessonIndex) { lessonSelected(course, root.selectedLesson) } } } model: LessonModel { id: lessonModel course: courseItem } onCurrentIndexChanged: { if (lessonModel.rowCount() > 0 && currentIndex != -1) { root.selectedLesson = lessonModel.data(lessonModel.index(currentIndex, 0), LessonModel.DataRole) } else { root.selectedLesson = null } } delegate: Item { id: item width: content.cellWidth height: content.cellHeight LessonSelectorItem { id: lessonItem anchors.fill: parent anchors.margins: 10 anchors.centerIn: parent lesson: dataRole selected: content.currentIndex == index editable: courseItem.editable onClicked: { item.forceActiveFocus() content.currentIndex = index } onDoubleClicked: { if (index <= course.lastUnlockedLessonIndex) { lessonSelected(course, dataRole) } } onStatButtonClicked: { statPopupDialog.open() } onEditButtonClicked: { lessonEditorDialog.open() } onDeleteButtonClicked: { course.deleteCustomLesson(); } } LessonLockedNotice { anchors.centerIn: parent visible: index > course.lastUnlockedLessonIndex glowColor: lessonItem.background.color } } } } Item { Layout.fillWidth: true height: footer.height z: 2 ToolBar { id: footer width: parent.width KColorScheme { id: footerColorScheme colorGroup: KColorScheme.Active colorSet: KColorScheme.Window } background: Rectangle { color: footerColorScheme.normalBackground } RowLayout { anchors.fill: parent spacing: 0 Item { Layout.fillWidth: true height: startButton.implicitHeight IconButton { id: startButton iconName: "go-next-view" bgColor: colorScheme.positiveBackground anchors.centerIn: parent text: i18n("Start Training") enabled: root.selectedLesson && content.currentIndex <= course.lastUnlockedLessonIndex onClicked: lessonSelected(course, root.selectedLesson) } } } } DropShadow { anchors.fill: footer source: footer samples: 16 horizontalOffset: 0 verticalOffset: 0 } } } }