diff --git a/src/qml/common/TextArea.qml b/src/qml/common/TextArea.qml new file mode 100644 index 0000000..9c36e13 --- /dev/null +++ b/src/qml/common/TextArea.qml @@ -0,0 +1,5 @@ +import QtQuick 2.0 + +Item { + +} diff --git a/src/qml/homescreen/HomeScreen.qml b/src/qml/homescreen/HomeScreen.qml index 4567fb5..a02e016 100644 --- a/src/qml/homescreen/HomeScreen.qml +++ b/src/qml/homescreen/HomeScreen.qml @@ -1,153 +1,154 @@ /* * 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.6 import QtQuick.Controls 2.2 import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 import ktouch 1.0 import "../common" FocusScope { id: screen property KeyboardLayout keyboardLayout property string keyboardLayoutName signal lessonSelected(variant course, variant lesson, variant profile) Connections { target: profileDataAccess onProfileCountChanged: findCurrentProfile() } function start() {} function reset() { profileDataAccess.loadProfiles(); } function findCurrentProfile() { profileComboBox.profile = null var lastProfileId = preferences.lastUsedProfileId for (var i = 0; i < profileDataAccess.profileCount; i++) { var profile = profileDataAccess.profile(i) if (profile.id === lastProfileId) { profileComboBox.profile = profile return; } } if (profileDataAccess.profileCount > 0) { profileComboBox.profile = profileDataAccess.profile(0) preferences.lastUsedProfileId = profileComboBox.profile.id preferences.writeConfig() } } function safeLastUsedProfile(profile) { preferences.lastUsedProfileId = profile.id preferences.writeConfig() } RowLayout { anchors.fill: parent spacing: 0 Item { id: navigationArea z: 2 Layout.preferredWidth: 300 Layout.fillHeight: true DropShadow { anchors.fill: navigationAreaLayout source: navigationAreaLayout samples: 16 horizontalOffset: 0 verticalOffset: 0 } ColumnLayout { id: navigationAreaLayout anchors.fill: parent spacing: 0 ToolBar { id: header Layout.fillWidth: true background: Rectangle { color: toolbarColorScheme.toolbarBackground } KColorScheme { id: toolbarColorScheme colorGroup: KColorScheme.Active colorSet: KColorScheme.Complementary property color toolbarBackground: Qt.darker(toolbarColorScheme.shade(toolbarColorScheme.hoverDecoration, KColorScheme.MidShade, toolbarColorScheme.contrast, -0.2), 1.3) } RowLayout { anchors.fill: parent spacing: 5 ProfileComboBox { id: profileComboBox colorScheme: toolbarColorScheme manageProfileButtonBgColor: toolbarColorScheme.toolbarBackground Layout.fillHeight: true Layout.preferredWidth: 300 Layout.fillWidth: true onActivated: { safeLastUsedProfile(profile) } } } } CourseSelector { id: courseSelector Layout.fillHeight: true Layout.fillWidth: true profile: profileComboBox.profile keyboardLayout: screen.keyboardLayout currentKeyboardLayoutName: screen.keyboardLayoutName } } } LessonSelector { Layout.fillHeight: true Layout.fillWidth: true profile: profileComboBox.profile + keyboardLayout: screen.keyboardLayout dataIndexCourse: courseSelector.selectedCourse onLessonSelected: screen.lessonSelected(course, lesson, profileComboBox.profile) z: 1 } } InitialProfileDialog { id: initialProfileForm visible: profileDataAccess.profileCount == 0 } } diff --git a/src/qml/homescreen/LessonEditorDialog.qml b/src/qml/homescreen/LessonEditorDialog.qml new file mode 100644 index 0000000..6f45dd7 --- /dev/null +++ b/src/qml/homescreen/LessonEditorDialog.qml @@ -0,0 +1,106 @@ +/* + * 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.6 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.2 +import ktouch 1.0 + +import '../common' + +PopupDialog { + id: root + + property Lesson lesson: Lesson {} + property KeyboardLayout keyboardLayout: KeyboardLayout {} + property Profile profile: Profile {} + + function editLesson(lesson) { + root.lesson.copyFrom(lesson) + root.open() + } + + onClosed: { + profileDataAccess.storeCustomLesson(root.lesson, root.profile, root.keyboardLayout.name) + } + + title: i18n("Edit lesson") + + margins: { + left: 40 + bottom: 40 + right: 40 + top: 40 + } + + width: parent.width - leftMargin - rightMargin + height: parent.height - topMargin - bottomMargin + + padding: titleLabel.font.pixelSize + + contentItem: GridLayout { + + columnSpacing: titleLabel.font.pixelSize + rowSpacing: titleLabel.font.pixelSize + + Label { + id: titleLabel + text: i18n("Title:") + Layout.row: 0 + Layout.column: 0 + } + + TextField { + id: titleTextField + text: root.lesson.title + onTextChanged: { + root.lesson.title = text + } + Layout.row: 0 + Layout.column: 1 + Layout.fillWidth: true + } + + ScrollView { + Layout.row: 1 + Layout.column: 0 + Layout.columnSpan: 2 + Layout.fillHeight: true + Layout.fillWidth: true + + TextArea { + id: lessonTextArea + text: root.lesson.text + onTextChanged: { + root.lesson.text = text + } + placeholderText: i18n("Lesson text") + font.family: "monospace" + } + } + } + + footer: IconButton { + text: i18n("Done") + icon: "dialog-ok" + bgColor: colorScheme.positiveBackground + onClicked: { + root.close() + } + + } +} diff --git a/src/qml/homescreen/LessonSelector.qml b/src/qml/homescreen/LessonSelector.qml index 5da0060..9512e10 100644 --- a/src/qml/homescreen/LessonSelector.qml +++ b/src/qml/homescreen/LessonSelector.qml @@ -1,332 +1,346 @@ /* * 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.4 import QtQuick.Controls 2.0 import QtQuick.Layouts 1.1 import ktouch 1.0 import QtGraphicalEffects 1.0 import "../common" ColumnLayout { id: root property Profile profile property DataIndexCourse dataIndexCourse + property KeyboardLayout keyboardLayout 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() } spacing: 0 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 break } } } } onProfileChanged: update() onDataIndexCourseChanged: { root.selectedLesson = null; course.update(); root.update(); } Course { id: courseItem property int lastUnlockedLessonIndex: -1 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 (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 } } } } Component.onCompleted: update() } StatPopupDialog { id: statPopupDialog profile: root.profile course: course lesson: root.selectedLesson } + LessonEditorDialog { + id: lesseonEditorDialog + profile: root.profile + keyboardLayout: root.keyboardLayout + onClosed: { + courseItem.update() + root.update() + } + } + Item { Layout.fillWidth: true Layout.preferredHeight: header.height z: 2 Column { id: header width: parent.width height: toolbar.height + courseDescriptionItem.height ToolBar { id: toolbar width: parent.width background: Rectangle { color: toolbarColorScheme.toolbarBackground } KColorScheme { id: toolbarColorScheme colorGroup: KColorScheme.Active colorSet: KColorScheme.Complementary property color toolbarBackground: Qt.darker(toolbarColorScheme.shade(toolbarColorScheme.hoverDecoration, KColorScheme.MidShade, toolbarColorScheme.contrast, -0.2), 1.5) } RowLayout { anchors.fill: parent anchors.leftMargin: 20 spacing: 5 Label { text: root.course? root.course.title: "" font.bold: true color: toolbarColorScheme.normalText } IconToolButton { id: toggleCourseDesciptionButton icon: "help-about" checkable: true color: toolbarColorScheme.normalText backgroundColor: toolbarColorScheme.normalBackground Layout.fillHeight: true Layout.preferredWidth: toolbar.height } Item { Layout.fillWidth: true } IconToolButton { id: configureButton icon: "application-menu" color: toolbarColorScheme.normalText backgroundColor: toolbarColorScheme.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 active: toggleCourseDesciptionButton.checked description: courseItem.description } } DropShadow { anchors.fill: header source: header samples: 16 horizontalOffset: 0 verticalOffset: 0 } } Item { Layout.fillHeight: true Layout.fillWidth: true z: 1 KColorScheme { id: gridColorScheme colorGroup: KColorScheme.Active colorSet: KColorScheme.View } Rectangle { anchors.fill: parent color: gridColorScheme.shade(gridColorScheme.normalBackground, KColorScheme.DarkShade, 1, 0.0) } GridView { id: content anchors.fill: parent anchors.leftMargin: 20 clip: true focus: true 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) { 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.topMargin: 10 anchors.leftMargin: 0 anchors.rightMargin: 20 anchors.bottomMargin: 10 anchors.centerIn: parent lesson: dataRole selected: content.currentIndex == index editButtonVisible: course.id == "custom_lessons" onClicked: { item.forceActiveFocus() content.currentIndex = index } onDoubleClicked: { if (index <= course.lastUnlockedLessonIndex) { lessonSelected(course, dataRole) } } onStatButtonClicked: { statPopupDialog.open() } + onEditButtonClicked: { + lesseonEditorDialog.editLesson(lesson) + } } LessonLockedNotice { anchors.centerIn: parent visible: index > course.lastUnlockedLessonIndex glowColor: lessonItem.background.color } } ScrollBar.vertical: ScrollBar { } } } 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 icon: "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 } } } diff --git a/src/qml/qml.qrc b/src/qml/qml.qrc index 4529622..da5738f 100644 --- a/src/qml/qml.qrc +++ b/src/qml/qml.qrc @@ -1,53 +1,54 @@ common/Balloon.qml common/DetailedRadioButton.qml common/IconButton.qml common/IconLabel.qml common/IconToolButton.qml common/InfoItem.qml common/InformationTable.qml common/InlineToolbar.qml common/LearningProgressChart.qml common/ListItem.qml common/MessageBox.qml common/MonochromeIcon.qml common/PopupDialog.qml common/SelectionGrip.qml common/SelectionRectangle.qml common/SheetDialog.qml homescreen/CourseDescriptionItem.qml homescreen/CourseSelector.qml homescreen/CourseSelectorKeyboardLayoutItem.qml homescreen/CourseSelectorKeyboardLayoutList.qml homescreen/CustomLessonSelector.qml homescreen/HomeScreen.qml homescreen/InitialProfileDialog.qml + homescreen/LessonEditorDialog.qml homescreen/LessonLockedNotice.qml homescreen/LessonPreview.qml homescreen/LessonSelector.qml homescreen/LessonSelectorBase.qml homescreen/LessonSelectorItem.qml homescreen/ProfileComboBox.qml homescreen/ProfileDetailsItem.qml homescreen/ProfileForm.qml homescreen/ProfileSelector.qml homescreen/StatPopupDialog.qml keyboard/KeyItem.qml keyboard/KeyLabel.qml keyboard/Keyboard.qml keyboard/KeyboardLayoutEditor.qml main.qml meters/AccuracyMeter.qml meters/CharactersPerMinuteMeter.qml meters/ElapsedTimeMeter.qml meters/Meter.qml meters/StatBox.qml scorescreen/ScoreScreen.qml trainingscreen/KeyboardUnavailableNotice.qml trainingscreen/TrainingScreen.qml trainingscreen/TrainingScreenMenuOverlay.qml trainingscreen/TrainingScreenToolbar.qml trainingscreen/TrainingWidget.qml