diff --git a/.gitignore b/.gitignore --- a/.gitignore +++ b/.gitignore @@ -4,3 +4,7 @@ /*.txt.user /*.kdev4 + +# Default build and install locations +/build +/install diff --git a/AUTHORS b/AUTHORS --- a/AUTHORS +++ b/AUTHORS @@ -1,3 +1,3 @@ -Sebastian Gottfried Håvard Frøiland Andreas Nicolai diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -23,14 +23,13 @@ include(ECMSetupVersion) include(FeatureSummary) -find_package(Qt5 5.5 REQUIRED COMPONENTS +find_package(Qt5 5.9 REQUIRED COMPONENTS Gui Qml Quick QuickWidgets - Script + QuickControls2 Sql - Test Widgets X11Extras Xml @@ -53,6 +52,7 @@ WidgetsAddons WindowSystem XmlGui + IconThemes ) option(COMPILE_QML "Precompile QML code" ON) @@ -65,7 +65,6 @@ ecm_optional_add_subdirectory(doc) ecm_optional_add_subdirectory(src) # ecm_optional_add_subdirectory(sounds) -ecm_optional_add_subdirectory(images) ecm_optional_add_subdirectory(icons) # files to install in the ktouch project root directory diff --git a/README.md b/README.md --- a/README.md +++ b/README.md @@ -1,5 +1,4 @@ -KTouch -====== +# KTouch [![Build Status](https://build.kde.org/view/Applications/job/Applications%20ktouch%20kf5-qt5%20SUSEQt5.9/badge/icon)](https://build.kde.org/view/Applications/job/Applications%20ktouch%20kf5-qt5%20SUSEQt5.9/) @@ -10,44 +9,93 @@ longer need to search for the keys on the keyboard you will be able to type quickly and accurately. -Building -======== +# Development Setup + +## Building KTouch has the following build dependencies: - * CMake >= 2.8.12 - * CMake Extra Modules >= 1.0.0 - * Qt >= 5.5 - * KDE Frameworks: - * Config - * ConfigWidgets - * CoreAddons - * Declarative - * DocTools - * I18n - * ItemViews - * KCMUtils - * KIO - * NewStuff - * TextEditor - * WidgetsAddons - * WindowSystem - * XmlGui - * libxkbfile (optional, for keyboard layout auto-detection) - * libxcb (optional, for keyboard layout auto-detection) - -If the build requirements are met, KTouch can be built and installed with: + + * [CMake][cmake] ≥ 3.0.0 + * [Extra CMake Modules][ecm] ≥ 1.0.0 + * [Qt][qt] ≥ 5.9 with the following modules + * Qt GUI + * Qt QML + * Qt Quick + * Qt Quick Widgets + * Qt Quick Controls 2 + * Qt SQL + * Qt Test + * Qt X11 Extras + * Qt XML + * Qt XML Patterns + * [KDE Frameworks 5][kf5] with: + * Completion + * Config + * ConfigWidgets + * CoreAddons + * Declarative + * DocTools + * I18n + * ItemViews + * KCMUtils + * KIO + * TextWidgets + * WidgetsAddons + * WindowSystem + * XmlGui + * IconThemes + * [libxkbfile][libxkbfile] (optional, for keyboard layout auto-detection) + * [libxcb][libxcb] (optional, for keyboard layout auto-detection) + +[cmake]: https://cmake.org/ +[ecm]: https://cgit.kde.org/extra-cmake-modules.git/ +[qt]: https://www.qt.io/ +[kf5]: https://api.kde.org/frameworks/ +[libxkbfile]: https://cgit.freedesktop.org/xorg/lib/libxkbfile +[libxcb]: https://xcb.freedesktop.org/ + +Collecting and installing all dependencies by hand can be a challenge. +Far easier is to use the dependency resolution of your package +manager. Usually they can be instructed to install all dependencies +automatically by using the information of the packaged version of +KTouch: + + apt-get build-dep ktouch # Ubuntu, Debian, ... + zypper source-install --build-deps-only # openSUSE + +If the build requirements are met, execute the following commands in +the root directory of your working copy to built and install KTouch: mkdir build - cd build/ - cmake .. + cd build + cmake .. -DCMAKE_INSTALL_PREFIX=../install make - sudo make install + make install + +These commands install KTouch to the directory `install` in the root of +your working copy. You can install KTouch to any directory of your +choice by passing a different path to `-DCMAKE_INSTALL_PREFIX`. + +## Running + +At runtime KTouch needs the following additional software packages to be +installed: + + * [Breeze icon set][breeze] + * [kqtquickcharts][kqtquickcharts] ≥ 16.12 + * [plasma-desktop][plasma-desktop] (optional, for keyboard layout + configuration inside the application) + +[breeze]: https://phabricator.kde.org/source/breeze-icons/ +[kqtquickcharts]: https://phabricator.kde.org/source/kqtquickcharts/ +[plasma-desktop]: https://phabricator.kde.org/source/plasma-desktop/ + +KTouch needs the following environment variables to be set in order +to find all its installed resources: -Runtime dependencies -==================== + export KDEDIRS=:$KDEDIRS + export XDG_DATA_DIRS=/share:$XDG_DATA_DIRS -At runtime KTouch needs the following software packages to be present: - * kqtquickcharts >= 16.12 (git://anongit.kde.org/kqtquickcharts) - * plasma-desktop (optional, for keyboard layout configuration - from within the application) +After this, KTouch can finally be launched with: + /bin/ktouch diff --git a/extras/scripts/checkqmlimports.py b/extras/scripts/checkqmlimports.py new file mode 100644 --- /dev/null +++ b/extras/scripts/checkqmlimports.py @@ -0,0 +1,97 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +# +# 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 argparse +import re +from pathlib import Path +from collections import namedtuple + +Import = namedtuple('Import', ['name', 'version', 'alias']) + +ALLOWED_IMPORTS = { + 'common': [ + Import('QtQuick.Controls', '2.2', None), + Import('QtQuick.Controls', '2.2', 'Controls'), + Import('org.kde.kquickcontrolsaddons', '2.0', 'Addons'), + ], + '*': [ + Import('QtQuick', '2.9', alias=None), + Import('QtQuick.Layouts', '1.3', alias=None), + Import('ktouch', '1.0', alias=None), + Import('QtGraphicalEffects', '1.0', alias=None), + Import('org.kde.charts', '0.1', alias='Charts'), + ], +} + +IMPORT_RE = re.compile(r'^import ([\w\.]+) ([\d\.]+)( as ([\w\.]+))?$') +LOCAL_IMPORT = re.compile('^import ("|\')[\w\./]+("|\')') + +def dir_arg(path_str): + path = Path(path_str) + if not path.exists(): + msg = "'{}' doesn't exist".format(path) + raise argparse.ArgumentTypeError(msg) + if not path.is_dir(): + msg = "'{}' has to be a directory".format(path) + raise argparse.ArgumentTypeError(msg) + return path + +def check_imports(file, module, local_path): + for line in file: + line = line.strip() + if not line.startswith('import'): + continue + if LOCAL_IMPORT.match(line) is not None: + continue + try: + check_package_import(line, module, local_path) + except ValueError: + print(f"{local_path}: unrecognized import statement: '{line}'") + + +def check_package_import(line, module, local_path): + matches = IMPORT_RE.match(line) + if matches is None: + raise ValueError('unrecognized import') + current_import = Import(*matches.group(1, 2, 4)) + for allowed_module, allowed_imports in ALLOWED_IMPORTS.items(): + if allowed_module == '*' or allowed_module == module: + for allowed_import in allowed_imports: + if current_import == allowed_import: + return + print(f"{local_path}: import not allowed here: '{line}'") + +if __name__ == '__main__': + parser = argparse.ArgumentParser( + description='check QML imports for KTouch' + ) + parser.add_argument('path', + type=dir_arg, + metavar="PATH", + default="" + ) + args = parser.parse_args() + for qml_file_path in args.path.glob('**/*.qml'): + local_path = qml_file_path.relative_to(args.path) + if len(local_path.parts) > 1: + module = local_path.parts[0] + else: + module = None + check_imports(qml_file_path.open(mode='r'), module, local_path) + diff --git a/images/CMakeLists.txt b/images/CMakeLists.txt deleted file mode 100644 --- a/images/CMakeLists.txt +++ /dev/null @@ -1,21 +0,0 @@ -set(image_files - accuracymeter-background.png - accuracymeter-scale.png - accuracymeter-hand.png - balloontip.svgz - charactersperminutemeter-background.png - charactersperminutemeter-scale.png - charactersperminutemeter-hand.png - elapsedtimemeter-background.png - elapsedtimemeter-minute-hand.png - elapsedtimemeter-second-hand.png - meterbox-left.png - meterbox-right.png - trainingscreen-footer.png - trainingscreen-header.png - trainingscreen-toolbar.png - trainingscreen-viewport.png - trainingscreen-viewport-shadow.png -) - -install(FILES ${image_files} DESTINATION ${DATA_INSTALL_DIR}/ktouch/images) diff --git a/images/charactersperminutemeter.svgz b/images/charactersperminutemeter.svgz deleted file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ +#include #include #include #include +#include #include #include @@ -30,7 +32,9 @@ #include "bindings/utils.h" #include "bindings/stringformatter.h" #include "declarativeitems/griditem.h" +#include "declarativeitems/kcolorschemeproxy.h" #include "declarativeitems/lessonpainter.h" +#include "declarativeitems/lessontexthighlighteritem.h" #include "declarativeitems/preferencesproxy.h" #include "declarativeitems/scalebackgrounditem.h" #include "declarativeitems/traininglinecore.h" @@ -59,6 +63,9 @@ registerQmlTypes(); migrateKde4Files(); + QIcon::setThemeName("breeze"); + QQuickStyle::setStyle("Default"); + DataAccess dataAccess; dataAccess.loadDataIndex(m_dataIndex); } @@ -81,7 +88,8 @@ { KDeclarative::KDeclarative kDeclarative; kDeclarative.setDeclarativeEngine(qmlEngine); - kDeclarative.setupBindings(); + kDeclarative.setupContext(); + kDeclarative.setupEngine(qmlEngine); Application* app = static_cast(Application::instance()); foreach (const QString& path, app->m_qmlImportPaths) @@ -124,10 +132,12 @@ qmlRegisterType("ktouch", 1, 0, "LearningProgressModel"); qmlRegisterType("ktouch", 1, 0, "ErrorsModel"); - qmlRegisterType("ktouch", 1, 0 , "Grid"); + qmlRegisterType("ktouch", 1, 0 , "LineGrid"); qmlRegisterType("ktouch", 1, 0, "ScaleBackgroundItem"); qmlRegisterType("ktouch", 1, 0, "LessonPainter"); + qmlRegisterType("ktouch", 1, 0, "LessonTextHighlighter"); qmlRegisterType("ktouch", 1, 0, "TrainingLineCore"); + qmlRegisterType("ktouch", 1, 0, "KColorScheme"); } void Application::migrateKde4Files() diff --git a/src/bindings/utils.h b/src/bindings/utils.h --- a/src/bindings/utils.h +++ b/src/bindings/utils.h @@ -21,16 +21,17 @@ #include #include #include +#include class Utils : public QObject { Q_OBJECT public: - explicit Utils(QObject* parent = 0); - Q_INVOKABLE QUrl findImage(const QString &name); + explicit Utils(QObject* parent = nullptr); Q_INVOKABLE int getMinutesOfQTime(const QTime& time); Q_INVOKABLE int getSecondsOfQTime(const QTime& time); Q_INVOKABLE QString uuid(); + Q_INVOKABLE QColor alpha(const QColor& color, float alpha); }; diff --git a/src/bindings/utils.cpp b/src/bindings/utils.cpp --- a/src/bindings/utils.cpp +++ b/src/bindings/utils.cpp @@ -17,6 +17,7 @@ #include "utils.h" +#include #include #include #include @@ -27,20 +28,6 @@ { } -QUrl Utils::findImage(const QString &name) -{ - const QString relPath = QStringLiteral("images/") + name; - const QString path = QStandardPaths::locate(QStandardPaths::DataLocation, relPath); - - if (path.isNull()) - { - qWarning() << "can't find image resource:" << name; - return QUrl(); - } - - return QUrl::fromLocalFile(path); -} - int Utils::getMinutesOfQTime(const QTime& time) { if (!time.isValid()) @@ -67,3 +54,10 @@ { return QUuid::createUuid().toString(); } + +QColor Utils::alpha(const QColor& color, float alpha) +{ + auto result = QColor(color); + result.setAlphaF(alpha * result.alphaF()); + return result; +} diff --git a/src/core/course.h b/src/core/course.h --- a/src/core/course.h +++ b/src/core/course.h @@ -32,30 +32,37 @@ Q_OBJECT Q_PROPERTY(DataIndexCourse* associatedDataIndexCourse READ associatedDataIndexCourse WRITE setAssociatedDataIndexCourse NOTIFY associatedDataIndexCourseChanged) Q_PROPERTY(int lessonCount READ lessonCount NOTIFY lessonCountChanged) - Q_PROPERTY(bool doSyncLessonCharacters READ doSyncLessonCharacters WRITE setDoSyncLessonCharacters NOTIFY doSyncLessonCharactersChanged) + Q_PROPERTY(Kind kind READ kind WRITE setKind NOTIFY kindChanged) public: + enum Kind { + SequentialCourse, + LessonCollection + }; + Q_ENUM(Kind) + explicit Course(QObject *parent = 0); DataIndexCourse* associatedDataIndexCourse() const; void setAssociatedDataIndexCourse(DataIndexCourse* dataIndexCourse); int lessonCount() const; - bool doSyncLessonCharacters() const; - void setDoSyncLessonCharacters(bool doSync); + Kind kind() const; + void setKind(Kind kind); void setId(const QString& id); void setTitle(const QString& title); void setDescription(const QString& description); void setKeyboardLayoutName(const QString& keyboardLayoutName); Q_INVOKABLE Lesson* lesson(int index) const; Q_INVOKABLE void addLesson(Lesson* lesson); Q_INVOKABLE void insertLesson(int index, Lesson* lesson); Q_INVOKABLE void removeLesson(int index); + Q_INVOKABLE int indexOfLesson(Lesson* lesson); Q_INVOKABLE void clearLessons(); Q_INVOKABLE void copyFrom(Course* source); signals: void associatedDataIndexCourseChanged(); void lessonCountChanged(); - void doSyncLessonCharactersChanged(); + void kindChanged(); void lessonAboutToBeAdded(Lesson* lesson, int index); void lessonAdded(); void lessonsAboutToBeRemoved(int first, int last); @@ -67,7 +74,7 @@ private: Q_DISABLE_COPY(Course) DataIndexCourse* m_associatedDataIndexCourse; - bool m_doSyncLessonCharacters; + Kind m_kind; QList m_lessons; QSignalMapper* m_signalMapper; }; diff --git a/src/core/course.cpp b/src/core/course.cpp --- a/src/core/course.cpp +++ b/src/core/course.cpp @@ -25,7 +25,7 @@ Course::Course(QObject *parent) : CourseBase(parent), m_associatedDataIndexCourse(0), - m_doSyncLessonCharacters(true), + m_kind(Course::SequentialCourse), m_signalMapper(new QSignalMapper(this)) { connect(m_signalMapper, SIGNAL(mapped(int)), SLOT(updateLessonCharacters(int))); @@ -45,17 +45,17 @@ } } -bool Course::doSyncLessonCharacters() const +Course::Kind Course::kind() const { - return m_doSyncLessonCharacters; + return m_kind; } -void Course::setDoSyncLessonCharacters(bool doSync) +void Course::setKind(Kind kind) { - if (doSync != m_doSyncLessonCharacters) + if (kind != m_kind) { - m_doSyncLessonCharacters = doSync; - emit doSyncLessonCharactersChanged(); + m_kind = kind; + emit kindChanged(); } } @@ -148,6 +148,11 @@ emit lessonsRemoved(); } +int Course::indexOfLesson(Lesson* lesson) +{ + return m_lessons.indexOf(lesson); +} + void Course::clearLessons() { if (m_lessons.count() == 0) @@ -167,7 +172,7 @@ setTitle(source->title()); setDescription(source->description()); setKeyboardLayoutName(source->keyboardLayoutName()); - setDoSyncLessonCharacters(source->doSyncLessonCharacters()); + setKind(source->kind()); clearLessons(); for (int i = 0; i < source->lessonCount(); i++) { @@ -180,8 +185,10 @@ void Course::updateLessonCharacters(int firstIndex) { - if (!m_doSyncLessonCharacters) + if (m_kind == Course::LessonCollection) + { return; + } QString characters = firstIndex > 0? lesson(firstIndex - 1)->characters(): QLatin1String(""); diff --git a/src/core/key.h b/src/core/key.h --- a/src/core/key.h +++ b/src/core/key.h @@ -38,6 +38,7 @@ void setFingerIndex(int finger); bool hasHapticMarker() const; void setHasHapticMarker(bool hasHapticMarker); + const QList& keyChars() const; int keyCharCount() const; Q_INVOKABLE KeyChar* keyChar(int index) const; Q_INVOKABLE void addKeyChar(KeyChar* keyChar); diff --git a/src/core/key.cpp b/src/core/key.cpp --- a/src/core/key.cpp +++ b/src/core/key.cpp @@ -61,6 +61,11 @@ } } +const QList& Key::keyChars() const +{ + return m_keyChars; +} + int Key::keyCharCount() const { return m_keyChars.count(); diff --git a/src/core/keyboardlayout.h b/src/core/keyboardlayout.h --- a/src/core/keyboardlayout.h +++ b/src/core/keyboardlayout.h @@ -56,6 +56,7 @@ Q_INVOKABLE void clearKeys(); AbstractKey* referenceKey(); Q_INVOKABLE void copyFrom(KeyboardLayout* source); + Q_INVOKABLE QString allCharacters() const; QSize size() const; void setSize(const QSize& size); diff --git a/src/core/keyboardlayout.cpp b/src/core/keyboardlayout.cpp --- a/src/core/keyboardlayout.cpp +++ b/src/core/keyboardlayout.cpp @@ -29,6 +29,7 @@ #include "abstractkey.h" #include "key.h" +#include "keychar.h" #include "specialkey.h" #include "dataindex.h" @@ -162,6 +163,23 @@ setIsValid(true); } +QString KeyboardLayout::allCharacters() const +{ + QString result; + for (const auto abstractKey: m_keys) + { + Key* const key = qobject_cast(abstractKey); + if (key) + { + for (const auto keyChar : key->keyChars()) + { + result.append(keyChar->value()); + } + } + } + return result; +} + AbstractKey* KeyboardLayout::key(int index) const { Q_ASSERT(index >= 0 && index < m_keys.count()); diff --git a/src/core/profiledataaccess.cpp b/src/core/profiledataaccess.cpp --- a/src/core/profiledataaccess.cpp +++ b/src/core/profiledataaccess.cpp @@ -623,7 +623,7 @@ if (!query.isActive()) return false; - target->setDoSyncLessonCharacters(false); + target->setKind(Course::LessonCollection); target->setId(QStringLiteral("custom_lessons")); target->setTitle(i18n("Custom Lessons")); target->setDescription(i18n("A place to store personal lesson texts")); diff --git a/src/core/resourcedataaccess.cpp b/src/core/resourcedataaccess.cpp --- a/src/core/resourcedataaccess.cpp +++ b/src/core/resourcedataaccess.cpp @@ -306,6 +306,7 @@ target->setTitle(root.firstChildElement(QStringLiteral("title")).text()); target->setDescription(root.firstChildElement(QStringLiteral("description")).text()); target->setKeyboardLayoutName(root.firstChildElement(QStringLiteral("keyboardLayout")).text()); + target->setKind(Course::SequentialCourse); target->clearLessons(); for (QDomElement lessonNode = root.firstChildElement(QStringLiteral("lessons")).firstChildElement(); diff --git a/src/core/specialkey.h b/src/core/specialkey.h --- a/src/core/specialkey.h +++ b/src/core/specialkey.h @@ -40,7 +40,7 @@ }; explicit SpecialKey(QObject *parent = 0); - Q_INVOKABLE QString keyType() const override; + Q_INVOKABLE QString keyType() const override; QString typeStr() const; void setTypeStr(const QString& typeStr); Type type() const; diff --git a/src/core/userdataaccess.cpp b/src/core/userdataaccess.cpp --- a/src/core/userdataaccess.cpp +++ b/src/core/userdataaccess.cpp @@ -130,6 +130,7 @@ target->setTitle(courseQuery.value(0).toString()); target->setDescription(courseQuery.value(1).toString()); target->setKeyboardLayoutName(courseQuery.value(2).toString()); + target->setKind(Course::SequentialCourse); target->clearLessons(); QSqlQuery lessonsQuery(db); diff --git a/src/declarativeitems/kcolorschemeproxy.h b/src/declarativeitems/kcolorschemeproxy.h new file mode 100644 --- /dev/null +++ b/src/declarativeitems/kcolorschemeproxy.h @@ -0,0 +1,135 @@ +/* + * 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 . + */ + +#ifndef KCOLORSCHEMEPROXY_H +#define KCOLORSCHEMEPROXY_H + +#include +#include +#include + +class KColorSchemeProxy : public QObject +{ + Q_OBJECT + Q_ENUMS(ColorGroup) + Q_ENUMS(ColorSet) + Q_ENUMS(ShadeRole) + + Q_PROPERTY(ColorGroup colorGroup READ colorGroup WRITE setColorGroup NOTIFY colorGroupChanged) + Q_PROPERTY(ColorSet colorSet READ colorSet WRITE setColorSet NOTIFY colorSetChanged) + + Q_PROPERTY(QColor normalBackground READ normalBackground NOTIFY paletteChanged) + Q_PROPERTY(QColor alternateBackground READ alternateBackground NOTIFY paletteChanged) + Q_PROPERTY(QColor activeBackground READ activeBackground NOTIFY paletteChanged) + Q_PROPERTY(QColor linkBackground READ linkBackground NOTIFY paletteChanged) + Q_PROPERTY(QColor visitedBackground READ visitedBackground NOTIFY paletteChanged) + Q_PROPERTY(QColor negativeBackground READ negativeBackground NOTIFY paletteChanged) + Q_PROPERTY(QColor neutralBackground READ neutralBackground NOTIFY paletteChanged) + Q_PROPERTY(QColor positiveBackground READ positiveBackground NOTIFY paletteChanged) + + Q_PROPERTY(QColor focusDecoration READ focusDecoration NOTIFY paletteChanged) + Q_PROPERTY(QColor hoverDecoration READ hoverDecoration NOTIFY paletteChanged) + + Q_PROPERTY(QColor normalText READ normalText NOTIFY paletteChanged) + Q_PROPERTY(QColor activeText READ activeText NOTIFY paletteChanged) + Q_PROPERTY(QColor linkText READ linkText NOTIFY paletteChanged) + Q_PROPERTY(QColor visitedText READ visitedText NOTIFY paletteChanged) + Q_PROPERTY(QColor negativeText READ negativeText NOTIFY paletteChanged) + Q_PROPERTY(QColor neutralText READ neutralText NOTIFY paletteChanged) + Q_PROPERTY(QColor positiveText READ positiveText NOTIFY paletteChanged) + + Q_PROPERTY(QColor lightShade READ lightShade NOTIFY paletteChanged) + Q_PROPERTY(QColor midlightShade READ midlightShade NOTIFY paletteChanged) + Q_PROPERTY(QColor midShade READ midShade NOTIFY paletteChanged) + Q_PROPERTY(QColor darkShade READ darkShade NOTIFY paletteChanged) + Q_PROPERTY(QColor shadowShade READ shadowShade NOTIFY paletteChanged) + + Q_PROPERTY(qreal contrast READ contrast NOTIFY paletteChanged) + +public: + enum ColorGroup { Active, Disabled, Inactive, NColorGroups, Current, All, Normal = Active }; + + enum ColorSet { + View, + Window, + Button, + Selection, + Tooltip, + Complementary + }; + + enum ShadeRole { + LightShade, + MidlightShade, + MidShade, + DarkShade, + ShadowShade + }; + + explicit KColorSchemeProxy(QObject* parent = 0); + + ColorGroup colorGroup() const; + void setColorGroup(ColorGroup group); + + + ColorSet colorSet() const; + void setColorSet(ColorSet colorSet); + + QColor normalBackground() const; + QColor alternateBackground() const; + QColor activeBackground() const; + QColor linkBackground() const; + QColor visitedBackground() const; + QColor negativeBackground() const; + QColor neutralBackground() const; + QColor positiveBackground() const; + + QColor focusDecoration() const; + QColor hoverDecoration() const; + + QColor normalText() const; + QColor inactiveText() const; + QColor activeText() const; + QColor linkText() const; + QColor visitedText() const; + QColor negativeText() const; + QColor neutralText() const; + QColor positiveText() const; + + QColor lightShade() const; + QColor midlightShade() const; + QColor midShade() const; + QColor darkShade() const; + QColor shadowShade() const; + + qreal contrast() const; + + Q_INVOKABLE QColor shade(const QColor &color, ShadeRole role) const; + Q_INVOKABLE QColor shade(const QColor &color, ShadeRole role, qreal contrast, qreal chromaAdjust = 0.0) const; + +signals: + void colorGroupChanged(); + void colorSetChanged(); + void paletteChanged(); + +private: + QPalette::ColorGroup m_colorGroup; + KColorScheme::ColorSet m_colorSet; + KColorScheme m_colorScheme; +}; + +#endif // KCOLORSCHEMEPROXY_H diff --git a/src/declarativeitems/kcolorschemeproxy.cpp b/src/declarativeitems/kcolorschemeproxy.cpp new file mode 100644 --- /dev/null +++ b/src/declarativeitems/kcolorschemeproxy.cpp @@ -0,0 +1,192 @@ +/* + * 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 . + */ + +#include "kcolorschemeproxy.h" + +#include + +KColorSchemeProxy::KColorSchemeProxy(QObject* parent) : + QObject(parent), + m_colorGroup(QPalette::Active), + m_colorSet(KColorScheme::View), + m_colorScheme(m_colorGroup, m_colorSet) +{ + auto app = qobject_cast(QCoreApplication::instance()); + if (app) + { + connect(app, SIGNAL(paletteChanged(QPalette)), SIGNAL(paletteChanged())); + } +} + +KColorSchemeProxy::ColorGroup KColorSchemeProxy::colorGroup() const +{ + return static_cast(m_colorGroup); +} + +void KColorSchemeProxy::setColorGroup(ColorGroup group) +{ + auto newColorGroup = static_cast(group); + if (newColorGroup != m_colorGroup) + { + m_colorGroup = newColorGroup; + emit colorGroupChanged(); + m_colorScheme = KColorScheme(m_colorGroup, m_colorSet); + emit paletteChanged(); + } +} + +KColorSchemeProxy::ColorSet KColorSchemeProxy::colorSet() const +{ + return static_cast(m_colorSet); +} + +void KColorSchemeProxy::setColorSet(ColorSet colorSet) +{ + auto newColorSet = static_cast(colorSet); + if (newColorSet != m_colorSet) + { + m_colorSet = newColorSet; + emit colorSetChanged(); + m_colorScheme = KColorScheme(m_colorGroup, m_colorSet); + emit paletteChanged(); + } +} + +QColor KColorSchemeProxy::normalBackground() const +{ + return m_colorScheme.background(KColorScheme::NormalBackground).color(); +} + +QColor KColorSchemeProxy::alternateBackground() const +{ + return m_colorScheme.background(KColorScheme::AlternateBackground).color(); +} + +QColor KColorSchemeProxy::activeBackground() const +{ + return m_colorScheme.background(KColorScheme::ActiveBackground).color(); +} + +QColor KColorSchemeProxy::linkBackground() const +{ + return m_colorScheme.background(KColorScheme::LinkBackground).color(); +} + +QColor KColorSchemeProxy::visitedBackground() const +{ + return m_colorScheme.background(KColorScheme::VisitedBackground).color(); +} + +QColor KColorSchemeProxy::negativeBackground() const +{ + return m_colorScheme.background(KColorScheme::NegativeBackground).color(); +} + +QColor KColorSchemeProxy::neutralBackground() const +{ + return m_colorScheme.background(KColorScheme::NeutralBackground).color(); +} + +QColor KColorSchemeProxy::positiveBackground() const +{ + return m_colorScheme.background(KColorScheme::PositiveBackground).color(); +} + +QColor KColorSchemeProxy::focusDecoration() const +{ + return m_colorScheme.decoration(KColorScheme::FocusColor).color(); +} + +QColor KColorSchemeProxy::hoverDecoration() const +{ + return m_colorScheme.decoration(KColorScheme::HoverColor).color(); +} + +QColor KColorSchemeProxy::normalText() const +{ + return m_colorScheme.foreground(KColorScheme::NormalText).color(); +} + +QColor KColorSchemeProxy::activeText() const +{ + return m_colorScheme.foreground(KColorScheme::ActiveText).color(); +} + +QColor KColorSchemeProxy::linkText() const +{ + return m_colorScheme.foreground(KColorScheme::LinkText).color(); +} + +QColor KColorSchemeProxy::visitedText() const +{ + return m_colorScheme.foreground(KColorScheme::VisitedText).color(); +} + +QColor KColorSchemeProxy::negativeText() const +{ + return m_colorScheme.foreground(KColorScheme::NegativeText).color(); +} + +QColor KColorSchemeProxy::neutralText() const +{ + return m_colorScheme.foreground(KColorScheme::NeutralText).color(); +} + +QColor KColorSchemeProxy::positiveText() const +{ + return m_colorScheme.foreground(KColorScheme::PositiveText).color(); +} + +QColor KColorSchemeProxy::lightShade() const +{ + return m_colorScheme.shade(KColorScheme::LightShade); +} + +QColor KColorSchemeProxy::midlightShade() const +{ + return m_colorScheme.shade(KColorScheme::MidlightShade); +} + +QColor KColorSchemeProxy::midShade() const +{ + return m_colorScheme.shade(KColorScheme::MidShade); +} + +QColor KColorSchemeProxy::darkShade() const +{ + return m_colorScheme.shade(KColorScheme::DarkShade); +} + +QColor KColorSchemeProxy::shadowShade() const +{ + return m_colorScheme.shade(KColorScheme::ShadowShade); +} + +qreal KColorSchemeProxy::contrast() const +{ + return KColorScheme::contrastF(); +} + +QColor KColorSchemeProxy::shade(const QColor& color, ShadeRole role) const +{ + return KColorScheme::shade(color, static_cast(role)); +} + +QColor KColorSchemeProxy::shade(const QColor& color, ShadeRole role, qreal contrast, qreal chromaAdjust) const +{ + return KColorScheme::shade(color, static_cast(role), contrast, chromaAdjust); +} diff --git a/src/declarativeitems/lessonpainter.h b/src/declarativeitems/lessonpainter.h --- a/src/declarativeitems/lessonpainter.h +++ b/src/declarativeitems/lessonpainter.h @@ -19,7 +19,7 @@ #define LESSONPAINTER_H #include -#include +#include #include @@ -77,7 +77,7 @@ qreal m_textScale; qreal m_maximumWidth; qreal m_maximumHeight; - QPixmap m_imageCache; + QImage m_imageCache; bool m_imageCacheDirty; TrainingLineCore* m_trainingLineCore; int m_currentLine; diff --git a/src/declarativeitems/lessonpainter.cpp b/src/declarativeitems/lessonpainter.cpp --- a/src/declarativeitems/lessonpainter.cpp +++ b/src/declarativeitems/lessonpainter.cpp @@ -181,8 +181,20 @@ void LessonPainter::paint(QPainter* painter) { - checkImageCache(); - painter->drawPixmap(0, 0, m_imageCache); + if (m_imageCacheDirty) + { + const auto device = painter->device(); + QImage img(QSize(qFloor(device->width()), qFloor(device->height())), QImage::Format_ARGB32_Premultiplied); + img.setDevicePixelRatio(img.width() / width()); + img.fill(Qt::transparent); + QPainter painter(&img); + painter.scale(m_textScale, m_textScale); + m_doc->drawContents(&painter); + m_imageCache = img; + m_imageCacheDirty = false; + } + + painter->drawImage(0, 0, m_imageCache); } void LessonPainter::updateLayout() @@ -317,15 +329,15 @@ void LessonPainter::invalidateImageCache() { m_imageCacheDirty = true; - m_imageCache = QPixmap(); + m_imageCache = QImage(); } void LessonPainter::checkImageCache() { if (!m_imageCacheDirty) return; - QPixmap img(qFloor(width()), qFloor(height())); + QImage img(QSize(qFloor(width()), qFloor(height())), QImage::Format_ARGB32_Premultiplied); img.fill(Qt::transparent); QPainter painter(&img); painter.scale(m_textScale, m_textScale); diff --git a/src/declarativeitems/lessontexthighlighteritem.h b/src/declarativeitems/lessontexthighlighteritem.h new file mode 100644 --- /dev/null +++ b/src/declarativeitems/lessontexthighlighteritem.h @@ -0,0 +1,45 @@ +/* + * 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 . + */ + +#ifndef LESSONTEXTHIGHLIGHTERITEM_H +#define LESSONTEXTHIGHLIGHTERITEM_H + +#include + +class LessonTextHighlighter; +class QQuickTextDocument; + +class LessonTextHighlighterItem : public QQuickItem +{ + Q_OBJECT + Q_PROPERTY(QString allowedCharacters READ allowedCharacters WRITE setAllowedCharacters NOTIFY allowedCharactersChanged) + Q_PROPERTY(QQuickTextDocument* document READ document WRITE setDocument NOTIFY documentChanged) +public: + LessonTextHighlighterItem(); + QString allowedCharacters() const; + void setAllowedCharacters(const QString& characters); + QQuickTextDocument* document() const; + void setDocument(QQuickTextDocument* document); +signals: + void allowedCharactersChanged(); + void documentChanged(); +private: + LessonTextHighlighter* m_highligher; + QQuickTextDocument* m_document; +}; + +#endif // LESSONTEXTHIGHLIGHTERITEM_H diff --git a/src/declarativeitems/lessontexthighlighteritem.cpp b/src/declarativeitems/lessontexthighlighteritem.cpp new file mode 100644 --- /dev/null +++ b/src/declarativeitems/lessontexthighlighteritem.cpp @@ -0,0 +1,63 @@ +/* + * 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 . + */ + +#include "lessontexthighlighteritem.h" + +#include + +#include "editor/lessontexthighlighter.h" + +LessonTextHighlighterItem::LessonTextHighlighterItem(): + m_highligher(new LessonTextHighlighter(this)) +{ +} + +QString LessonTextHighlighterItem::allowedCharacters() const +{ + return m_highligher->allowedCharacters(); +} + +void LessonTextHighlighterItem::setAllowedCharacters(const QString& characters) +{ + if (characters != m_highligher->allowedCharacters()) + { + m_highligher->setAllowedCharacters(characters); + emit allowedCharactersChanged(); + } +} + +QQuickTextDocument* LessonTextHighlighterItem::document() const +{ + return m_document; +} + +void LessonTextHighlighterItem::setDocument(QQuickTextDocument* document) +{ + if (document != m_document) + { + m_document = document; + if (document != nullptr) + { + m_highligher->setDocument(document->textDocument()); + } + else + { + m_highligher->setDocument(nullptr); + } + emit documentChanged(); + } +} diff --git a/src/editor/charactersviewdelegate.h b/src/editor/charactersviewdelegate.h --- a/src/editor/charactersviewdelegate.h +++ b/src/editor/charactersviewdelegate.h @@ -26,10 +26,10 @@ { Q_OBJECT public: - explicit CharactersViewDelegate(QObject* parent = 0); + explicit CharactersViewDelegate(QObject* parent = nullptr); KeyboardLayout* keyboardLayout() const; void setKeyboardLayout(KeyboardLayout* keyboardLayout); - QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override; + QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option, const QModelIndex& index) const override; void setEditorData(QWidget* editor, const QModelIndex& index) const override; void setModelData(QWidget *editor, QAbstractItemModel* model, const QModelIndex& index) const override; private: diff --git a/src/editor/keyboardlayouteditor.cpp b/src/editor/keyboardlayouteditor.cpp --- a/src/editor/keyboardlayouteditor.cpp +++ b/src/editor/keyboardlayouteditor.cpp @@ -52,7 +52,7 @@ Application::setupDeclarativeBindings(m_view->engine()); m_view->rootContext()->setContextProperty(QStringLiteral("keyboardLayoutEditor"), this); - m_view->setSource(QUrl(QStringLiteral("qrc:/qml/keyboard/KeyboardLayoutEditor.qml"))); + m_view->setSource(QUrl(QStringLiteral("qrc:/ktouch/qml/keyboard/KeyboardLayoutEditor.qml"))); connect(m_newKeyToolButton, &QAbstractButton::clicked, this, &KeyboardLayoutEditor::createNewKey); connect(m_newSpecialKeyToolButton, &QAbstractButton::clicked, this, &KeyboardLayoutEditor::createNewSpecialKey); diff --git a/src/editor/keyboardlayouteditorview.h b/src/editor/keyboardlayouteditorview.h --- a/src/editor/keyboardlayouteditorview.h +++ b/src/editor/keyboardlayouteditorview.h @@ -24,7 +24,7 @@ { Q_OBJECT public: - explicit KeyboardLayoutEditorView(QWidget* parent = 0); + explicit KeyboardLayoutEditorView(QWidget* parent = nullptr); signals: void clicked(); protected: diff --git a/src/editor/lessontexthighlighter.h b/src/editor/lessontexthighlighter.h --- a/src/editor/lessontexthighlighter.h +++ b/src/editor/lessontexthighlighter.h @@ -27,7 +27,7 @@ Q_OBJECT public: - explicit LessonTextHighlighter(QObject* parent = 0); + explicit LessonTextHighlighter(QObject* parent = nullptr); int maximumLineLength() const; void setMaximumLineLength(int length); QString allowedCharacters() const; diff --git a/src/editor/lessontexthighlighter.cpp b/src/editor/lessontexthighlighter.cpp --- a/src/editor/lessontexthighlighter.cpp +++ b/src/editor/lessontexthighlighter.cpp @@ -20,7 +20,8 @@ #include LessonTextHighlighter::LessonTextHighlighter(QObject* parent): - QSyntaxHighlighter(parent) + QSyntaxHighlighter(parent), + m_maximumLineLength(60) { KColorScheme inactiveScheme(QPalette::Inactive, KColorScheme::View); KColorScheme activeScheme(QPalette::Active, KColorScheme::View); @@ -63,9 +64,9 @@ { const QLatin1Char space(32); - if (text.length() > 60) + if (text.length() > m_maximumLineLength) { - setFormat(60, text.length() - 60, m_overLongLineFormat); + setFormat(m_maximumLineLength, text.length() - m_maximumLineLength, m_overLongLineFormat); } if (m_allowedCharacters.isNull()) diff --git a/images/accuracymeter-background.png b/src/images/accuracymeter-background.png rename from images/accuracymeter-background.png rename to src/images/accuracymeter-background.png diff --git a/src/images/accuracymeter-background@2x.png b/src/images/accuracymeter-background@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ + + accuracymeter-background.png + accuracymeter-hand.png + accuracymeter-scale.png + meterbox-left.png + meterbox-right.png + statusled.svgz + trainingscreen-footer.png + trainingscreen-header.png + trainingscreen-toolbar.png + trainingscreen-viewport-shadow.png + trainingscreen-viewport.png + balloontip.svgz + charactersperminutemeter-background.png + charactersperminutemeter-hand.png + charactersperminutemeter-scale.png + elapsedtimemeter-background.png + elapsedtimemeter-background@2x.png + elapsedtimemeter-minute-hand.png + elapsedtimemeter-second-hand.png + elapsedtimemeter-minute-hand@2x.png + elapsedtimemeter-second-hand@2x.png + images.qrc + meterbox-left@2x.png + meterbox-right@2x.png + accuracymeter-background@2x.png + accuracymeter-hand@2x.png + accuracymeter-scale@2x.png + charactersperminutemeter-background@2x.png + charactersperminutemeter-scale@2x.png + + diff --git a/images/meterbox-left.png b/src/images/meterbox-left.png rename from images/meterbox-left.png rename to src/images/meterbox-left.png diff --git a/src/images/meterbox-left@2x.png b/src/images/meterbox-left@2x.png new file mode 100644 index 0000000000000000000000000000000000000000..0000000000000000000000000000000000000000 GIT binary patch literal 0 Hc$@ #include @@ -89,11 +90,21 @@ return Application::dataIndex(); } +bool KTouchContext::keyboardKCMAvailable() +{ + return testKCMAvailibility(keyboardKCMName); +} + void KTouchContext::showMenu(int xPos, int yPos) { m_menu->popup(m_view->mapToGlobal(QPoint(xPos, yPos))); } +Lesson* KTouchContext::createLesson() +{ + return new Lesson(); +} + void KTouchContext::showResourceEditor() { QPointer& resourceEditorRef = Application::resourceEditorRef(); @@ -142,7 +153,7 @@ KShortcutsDialog::configure(m_actionCollection, KShortcutsEditor::LetterShortcutsDisallowed, m_mainWindow); } -void KTouchContext::configureKeyboard() +void KTouchContext::showKeyboardKCM() { QPointer kcm = new KCMultiDialog(m_mainWindow); @@ -177,7 +188,7 @@ { QAction* configureKeyboardAction = new QAction(i18n("Configure Keyboard..."), this); m_menu->addAction(configureKeyboardAction); - connect(configureKeyboardAction, &QAction::triggered, this, &KTouchContext::configureKeyboard); + connect(configureKeyboardAction, &QAction::triggered, this, &KTouchContext::showKeyboardKCM); } #else m_menu->addMenu(m_keyboardLayoutMenu); diff --git a/src/main.cpp b/src/main.cpp --- a/src/main.cpp +++ b/src/main.cpp @@ -26,6 +26,9 @@ int main(int argc, char **argv) { + Application::setAttribute(Qt::AA_EnableHighDpiScaling); + Application::setAttribute(Qt::AA_UseHighDpiPixmaps); + Application app(argc, argv); KLocalizedString::setApplicationDomain("ktouch"); diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -54,7 +54,7 @@ m_view->connect(m_view, &QQuickView::statusChanged, this, &MainWindow::onViewStatusChanged); m_view->rootContext()->setContextProperty(QStringLiteral("ktouch"), m_context); m_view->setResizeMode(QQuickView::SizeRootObjectToView); - m_view->setSource(QUrl(QStringLiteral("qrc:/qml/main.qml"))); + m_view->setSource(QUrl(QStringLiteral("qrc:/ktouch/qml/main.qml"))); } void MainWindow::onViewStatusChanged(QQuickView::Status status) diff --git a/src/models/categorizedresourcesortfilterproxymodel.h b/src/models/categorizedresourcesortfilterproxymodel.h --- a/src/models/categorizedresourcesortfilterproxymodel.h +++ b/src/models/categorizedresourcesortfilterproxymodel.h @@ -28,25 +28,30 @@ Q_OBJECT Q_PROPERTY(ResourceModel::ResourceItemTypes resourceTypeFilter READ resourceTypeFilter WRITE setResourceTypeFilter NOTIFY resourceTypeFilterChanged) Q_PROPERTY(QString keyboardLayoutNameFilter READ keyboardLayoutNameFilter WRITE setKeyboardLayoutNameFilter NOTIFY keyboardLayoutNameFilterChanged) + Q_PROPERTY(bool invertedKeyboardLayoutNameFilter READ invertedKeyboardLayoutNameFilter WRITE setInvertedKeyboardLayoutNameFilter NOTIFY invertedKeyboardLayoutNameFilterChanged) Q_PROPERTY(ResourceModel* resourceModel READ resourceModel WRITE setResourceModel NOTIFY resourceModelChanged) public: - explicit CategorizedResourceSortFilterProxyModel(QObject* parent = 0); + explicit CategorizedResourceSortFilterProxyModel(QObject* parent = nullptr); ResourceModel::ResourceItemTypes resourceTypeFilter() const; void setResourceTypeFilter(ResourceModel::ResourceItemTypes types); QString keyboardLayoutNameFilter() const; void setKeyboardLayoutNameFilter(const QString& name); + bool invertedKeyboardLayoutNameFilter() const; + void setInvertedKeyboardLayoutNameFilter(bool inverted); ResourceModel* resourceModel() const; void setResourceModel(ResourceModel* resourceModel); signals: void resourceTypeFilterChanged(); void keyboardLayoutNameFilterChanged(); + void invertedKeyboardLayoutNameFilterChanged(); void resourceModelChanged(); protected: bool subSortLessThan(const QModelIndex& left, const QModelIndex& right) const override; bool filterAcceptsRow(int source_row, const QModelIndex& source_parent) const override; private: ResourceModel::ResourceItemTypes m_resourceTypeFilter; QString m_keyboardLayoutNameFilter; + bool m_invertedKeyboardLayoutNameFilter; ResourceModel* m_resourceModel; }; diff --git a/src/models/categorizedresourcesortfilterproxymodel.cpp b/src/models/categorizedresourcesortfilterproxymodel.cpp --- a/src/models/categorizedresourcesortfilterproxymodel.cpp +++ b/src/models/categorizedresourcesortfilterproxymodel.cpp @@ -23,6 +23,7 @@ CategorizedResourceSortFilterProxyModel::CategorizedResourceSortFilterProxyModel(QObject *parent) : KCategorizedSortFilterProxyModel(parent), m_resourceTypeFilter(ResourceModel::CourseItem | ResourceModel::KeyboardLayoutItem), + m_invertedKeyboardLayoutNameFilter(false), m_resourceModel(0) { setDynamicSortFilter(true); @@ -62,10 +63,29 @@ } } +bool CategorizedResourceSortFilterProxyModel::invertedKeyboardLayoutNameFilter() const +{ + return m_invertedKeyboardLayoutNameFilter; +} + +void CategorizedResourceSortFilterProxyModel::setInvertedKeyboardLayoutNameFilter(bool inverted) +{ + if (inverted != m_invertedKeyboardLayoutNameFilter) + { + m_invertedKeyboardLayoutNameFilter = inverted; + invalidateFilter(); + invalidate(); + sort(0); + emit invertedKeyboardLayoutNameFilterChanged(); + } +} + + ResourceModel* CategorizedResourceSortFilterProxyModel::resourceModel() const { return m_resourceModel; } + void CategorizedResourceSortFilterProxyModel::setResourceModel(ResourceModel* resourceModel) { if (resourceModel != m_resourceModel) @@ -107,5 +127,5 @@ const QString name = sourceModel()->data(index, ResourceModel::KeyboardLayoutNameRole).toString(); - return name == m_keyboardLayoutNameFilter; + return m_invertedKeyboardLayoutNameFilter ^ (name == m_keyboardLayoutNameFilter); } diff --git a/src/models/charactersmodel.h b/src/models/charactersmodel.h --- a/src/models/charactersmodel.h +++ b/src/models/charactersmodel.h @@ -32,7 +32,7 @@ Q_PROPERTY(KeyboardLayout* keyboardLayout READ keyboardLayout WRITE setKeyboardLayout NOTIFY keyboardLayoutChanged) Q_PROPERTY(int keyIndex READ keyIndex WRITE setKeyIndex NOTIFY keyIndexChanged) public: - explicit CharactersModel(QObject *parent = 0); + explicit CharactersModel(QObject *parent = nullptr); KeyboardLayout* keyboardLayout() const; void setKeyboardLayout(KeyboardLayout* keyboardLayout); int keyIndex() const; diff --git a/src/models/charactersmodel.cpp b/src/models/charactersmodel.cpp --- a/src/models/charactersmodel.cpp +++ b/src/models/charactersmodel.cpp @@ -28,10 +28,10 @@ CharactersModel::CharactersModel(QObject *parent) : QAbstractTableModel(parent), - m_keyboardLayout(0), + m_keyboardLayout(nullptr), m_keyIndex(-1), - m_key(0), - m_undoStack(0), + m_key(nullptr), + m_undoStack(nullptr), m_signalMapper(new QSignalMapper(this)) { connect(m_signalMapper, SIGNAL(mapped(int)), SLOT(emitCharacterChanged(int))); @@ -62,7 +62,7 @@ if (!m_keyboardLayout) return; - Key* key = 0; + Key* key = nullptr; if (keyIndex != -1) { @@ -168,7 +168,7 @@ return false; KeyChar* keyChar = m_key->keyChar(index.row()); - QUndoCommand* cmd = 0; + QUndoCommand* cmd = nullptr; switch (index.column()) { @@ -279,7 +279,6 @@ case Qt::DisplayRole: case Qt::EditRole: return keyChar->value(); - return keyChar->value(); default: return QVariant(); } @@ -315,6 +314,7 @@ case KeyChar::Hidden: return i18n("Hidden"); } + return QVariant(); case Qt::EditRole: return QVariant(keyChar->position()); default: diff --git a/src/models/errorsmodel.h b/src/models/errorsmodel.h --- a/src/models/errorsmodel.h +++ b/src/models/errorsmodel.h @@ -28,7 +28,7 @@ Q_PROPERTY(TrainingStats* trainingStats READ trainingStats WRITE setTrainingStats NOTIFY trainingStatsChanged) Q_PROPERTY(int maximumErrorCount READ maximumErrorCount NOTIFY maximumErrorCountChanged) public: - explicit ErrorsModel(QObject* parent = 0); + explicit ErrorsModel(QObject* parent = nullptr); TrainingStats* trainingStats() const; void setTrainingStats(TrainingStats* trainingStats); int maximumErrorCount() const; diff --git a/src/models/learningprogressmodel.h b/src/models/learningprogressmodel.h --- a/src/models/learningprogressmodel.h +++ b/src/models/learningprogressmodel.h @@ -34,7 +34,7 @@ Q_PROPERTY(int maxCharactersTypedPerMinute READ maxCharactersTypedPerMinute NOTIFY maxCharactersTypedPerMinuteChanged) Q_PROPERTY(qreal minAccuracy READ minAccuracy NOTIFY minAccuracyChanged) public: - explicit LearningProgressModel(QObject* parent = 0); + explicit LearningProgressModel(QObject* parent = nullptr); Profile* profile() const; void setProfile(Profile* profile); Course* courseFilter() const; @@ -45,6 +45,7 @@ qreal minAccuracy() const; int columnCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + Q_INVOKABLE QDateTime date(int row) const; Q_INVOKABLE int charactersPerMinute(int row) const; Q_INVOKABLE int charactersTyped(int row) const; Q_INVOKABLE int errorCount(int row) const; diff --git a/src/models/learningprogressmodel.cpp b/src/models/learningprogressmodel.cpp --- a/src/models/learningprogressmodel.cpp +++ b/src/models/learningprogressmodel.cpp @@ -186,6 +186,13 @@ } } +QDateTime LearningProgressModel::date(int row) const +{ + LearningProgressModel* model = const_cast(this); + QSqlRecord record = model->record(row); + return QDateTime::fromMSecsSinceEpoch(record.value(0).value()); +} + int LearningProgressModel::charactersPerMinute(int row) const { const int charactersTyped = this->charactersTyped(row); diff --git a/src/models/lessonmodel.h b/src/models/lessonmodel.h --- a/src/models/lessonmodel.h +++ b/src/models/lessonmodel.h @@ -28,13 +28,18 @@ { Q_OBJECT Q_PROPERTY(Course* course READ course WRITE setCourse NOTIFY courseChanged) + Q_ENUMS(AdditionalRoles) public: - explicit LessonModel(QObject* parent = 0); + enum AdditionalRoles { + DataRole = Qt::UserRole + 1 + }; + explicit LessonModel(QObject* parent = nullptr); Course* course() const; void setCourse(Course* course); QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override; int rowCount(const QModelIndex& parent = QModelIndex()) const override; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const override; + QHash roleNames() const override; signals: void courseChanged(); diff --git a/src/models/lessonmodel.cpp b/src/models/lessonmodel.cpp --- a/src/models/lessonmodel.cpp +++ b/src/models/lessonmodel.cpp @@ -82,6 +82,8 @@ QVariant(lesson->title()): QVariant(i18n("")); case Qt::ToolTipRole: return QVariant(i18n("

New characters: %1

%2

", lesson->newCharacters(), lesson->text())); + case LessonModel::DataRole: + return QVariant::fromValue(lesson); default: return QVariant(); } @@ -138,6 +140,13 @@ return QVariant(i18n("Title")); } +QHash LessonModel::roleNames() const +{ + QHash names = QAbstractItemModel::roleNames(); + names.insert(LessonModel::DataRole, "dataRole"); + return names; +} + void LessonModel::updateMappings() { for (int i = 0; i < m_course->lessonCount(); i++) diff --git a/src/models/resourcemodel.h b/src/models/resourcemodel.h --- a/src/models/resourcemodel.h +++ b/src/models/resourcemodel.h @@ -47,7 +47,7 @@ SourceRole }; Q_ENUM(AdditionalRoles) - explicit ResourceModel( QObject* parent = 0); + explicit ResourceModel( QObject* parent = nullptr); DataIndex* dataIndex() const; void setDataIndex(DataIndex* dataIndex); Qt::ItemFlags flags(const QModelIndex& index) const override; diff --git a/src/qml/common/AutoTriggerButton.qml b/src/qml/common/AutoTriggerButton.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/AutoTriggerButton.qml @@ -0,0 +1,56 @@ +/* + * 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.Controls 2.2 +import ktouch 1.0 + +IconButton { + id: control + + property alias duration: progressAnimation.duration + property alias running: progressAnimation.running + + background.children: [ + ProgressBar { + id: progressBar + anchors.fill: parent + value: 0.0 + background: Item {} + contentItem: Item { + Rectangle { + color: control.colorScheme.focusDecoration + opacity: 0.4 + width: parent.width * progressBar.visualPosition + height: parent.height + } + } + + NumberAnimation { + id: progressAnimation + target: progressBar + property: "value" + duration: 10000 + to: 1.0 + running: true + onStopped: { + control.clicked(); + } + } + } + ] +} diff --git a/src/qml/common/Balloon.qml b/src/qml/common/Balloon.qml --- a/src/qml/common/Balloon.qml +++ b/src/qml/common/Balloon.qml @@ -18,7 +18,7 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ -import QtQuick 2.4 +import QtQuick 2.9 import QtGraphicalEffects 1.0 Loader { @@ -76,10 +76,10 @@ color: palette.alternateBase radius: 5 - property variant parentPos: root.visualParent? root.visualParent.mapToItem(dismissArea, 0, 0): Qt.point(0, 0) + property variant parentPos: root.visualParent? root.visualParent.mapToItem(null, 0, 0): Qt.point(0, 0) property bool under: root.visualParent ? internal.parentPos.y + root.visualParent.height + height < dismissArea.height : true - //bindings won't work inside anchors definition + // bindings don't work for anchor definition onUnderChanged: { if (under) { balloonTip.anchors.top = undefined @@ -123,7 +123,7 @@ id: balloonTipMask anchors.fill: balloonTip visible: false - source: utils.findImage("balloontip.svgz") + source: "qrc:///ktouch/images/balloontip.svgz" sourceSize: Qt.size(width, height) } diff --git a/src/qml/common/Collapsable.qml b/src/qml/common/Collapsable.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/Collapsable.qml @@ -0,0 +1,72 @@ +/* + * 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.Controls 2.2 as Controls +import ktouch 1.0 + +Controls.Control { + id: root + + property bool collapsed: false + default property Item data + + signal contentReadyForSwap + + function swapContent() { + contentItem.isSwapping = true + } + + property KColorScheme colorScheme: KColorScheme { + colorGroup: KColorScheme.Active + colorSet: KColorScheme.Window + } + + height: collapsed && contentItem.opacity == 0? 0: contentItem.implicitHeight + visible: height > 0 + + Behavior on height { + NumberAnimation { + duration: 150 + easing.type: Easing.InOutQuad + } + } + + background: Rectangle { + color: colorScheme.normalBackground + } + + contentItem: Item { + property bool isSwapping: false + opacity: !root.collapsed && !isSwapping && root.height === implicitHeight? 1: 0 + data: root.data + implicitHeight: children.length > 0? children[0].implicitHeight: 0 + + Behavior on opacity { + NumberAnimation { + duration: 150 + } + } + + onOpacityChanged: { + if (isSwapping && opacity === 0) { + contentReadyForSwap() + isSwapping = false; + } + } + } +} diff --git a/src/qml/common/ComboBox.qml b/src/qml/common/ComboBox.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/ComboBox.qml @@ -0,0 +1,131 @@ +/* + * 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.Controls 2.2 +import QtGraphicalEffects 1.0 +import ktouch 1.0 + +ComboBox { + id: control + + property string iconRole: "icon" + + property KColorScheme colorScheme: KColorScheme { + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Inactive + colorSet: KColorScheme.Button + } + + property alias popupListView: list + + KColorScheme { + id: listColorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.View + } + + hoverEnabled: true + spacing: 0.7 * font.pixelSize + + background: Item { + Rectangle { + anchors.fill: parent + opacity: (popup.visible? 0.6: 0.0) + (control.hovered || control.visualFocus? 0.3: 0.0) + color: colorScheme.normalBackground + Behavior on opacity { + NumberAnimation { + duration: 150 + } + } + } + + FocusBar { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 3 + control: control + } + } + + contentItem: IconLabel { + text: control.displayText + iconName: currentIndex != -1 && currentIndex < model.count ? control.model.get(currentIndex)[control.iconRole]: "" + color: colorScheme.normalText + } + + indicator: MonochromeIcon { + color: colorScheme.normalText + icon: "arrow-down-double" + x: control.width - width - control.contentItem.padding + y: control.topPadding + (control.availableHeight - height) / 2 + rotation: control.popup.opacity * -180 + } + + delegate: ListItem { + width: control.width + text: control.textRole ? (Array.isArray(control.model) ? modelData[control.textRole] : model[control.textRole]) : modelData + iconName: control.iconRole ? (Array.isArray(control.model) ? modelData[control.iconRole] : model[control.iconRole]) : "" + highlighted: control.currentIndex === index + } + + popup: Popup { + y: control.height + width: control.width + implicitHeight: contentItem.implicitHeight + padding: 0 + opacity: 0 + + enter: Transition { + NumberAnimation { + property: "opacity" + to: 1.0 + duration: 150 + } + } + + exit: Transition { + NumberAnimation { + property: "opacity" + to: 0.0 + duration: 150 + } + } + + contentItem: + ListView { + id: list + clip: true + implicitHeight: contentHeight + width: parent.width + model: control.popup.visible ? control.delegateModel : null + currentIndex: control.highlightedIndex + } + + background: Rectangle { + color: listColorScheme.normalBackground + layer.enabled: true + layer.effect: DropShadow { + samples: 24 + horizontalOffset: 0 + verticalOffset: 0 + } + } + } +} diff --git a/src/qml/common/DetailedRadioButton.qml b/src/qml/common/DetailedRadioButton.qml deleted file mode 100644 --- a/src/qml/common/DetailedRadioButton.qml +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright 2014 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 1.3 -import QtQuick.Layouts 1.1 - -ColumnLayout { - id: root - - property alias checked: radioButton.checked - property alias enabled: radioButton.enabled - property alias label: label.text - property alias hint: hint.text - - spacing: 10 - - Row { - id: radioButtonRow - Layout.fillWidth: true - spacing: Math.round(label.height / 4) - - RadioButton { - id: radioButton - anchors.verticalCenter: parent.verticalCenter - } - - Label { - id: label - anchors.verticalCenter: parent.verticalCenter - /* - * The text wrapping of the label doesn't work if it is invisible - * (wrapped at every character), hence the following hack. - */ - width: visible? parent.width - radioButton.width - parent.spacing: 0 - wrapMode: Text.Wrap - opacity: radioButton.opacity - MouseArea { - anchors.fill: parent - enabled: radioButton.enabled - onClicked: { - radioButton.checked = true - } - } - } - } - - Row { - Layout.fillWidth: true - spacing: radioButtonRow.spacing - - Item { - width: radioButton.width - height: hint.height - } - - Label { - id: hint - font.italic: true - width: visible? parent.width - radioButton.width - parent.spacing: 0 - wrapMode: Text.Wrap - opacity: radioButton.opacity - } - } -} diff --git a/src/qml/common/InfoItem.qml b/src/qml/common/FocusBar.qml copy from src/qml/common/InfoItem.qml copy to src/qml/common/FocusBar.qml --- a/src/qml/common/InfoItem.qml +++ b/src/qml/common/FocusBar.qml @@ -1,5 +1,5 @@ /* - * Copyright 2012 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 @@ -15,10 +15,20 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 +import QtQuick.Controls 2.2 as Controls +import ktouch 1.0 -QtObject { - property string title - property string text -} +Rectangle { + property Item control + + height: 2 + color: control.colorScheme.focusDecoration + opacity: control.activeFocus? 1: 0 + Behavior on opacity { + NumberAnimation { + duration: 150 + } + } +} diff --git a/src/qml/common/GridView.qml b/src/qml/common/GridView.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/GridView.qml @@ -0,0 +1,62 @@ +/* + * 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.Controls 2.2 as Controls +import ktouch 1.0 + +GridView { + id: control + + property alias colorScheme: colorScheme + property alias background: backgroundItem + + KColorScheme { + id: colorScheme + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet: KColorScheme.View + } + + Rectangle { + id: backgroundItem + anchors.fill: parent + color: colorScheme.normalBackground + z: -1 + } + + FocusBar { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: 3 + control: control + } + + FocusBar { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 3 + control: control + } + + Controls.ScrollBar.vertical: ScrollBar { } +} diff --git a/src/qml/common/InfoItem.qml b/src/qml/common/Icon.qml copy from src/qml/common/InfoItem.qml copy to src/qml/common/Icon.qml --- a/src/qml/common/InfoItem.qml +++ b/src/qml/common/Icon.qml @@ -1,5 +1,5 @@ /* - * Copyright 2012 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 @@ -15,10 +15,8 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import org.kde.kquickcontrolsaddons 2.0 as Addons -QtObject { - property string title - property string text -} +Addons.QIconItem { +} diff --git a/src/qml/common/IconButton.qml b/src/qml/common/IconButton.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/IconButton.qml @@ -0,0 +1,88 @@ +/* + * 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.Controls 2.2 +import ktouch 1.0 +import QtGraphicalEffects 1.0 + +Button { + id: button + + property alias color: content.color + property alias bgColor: bg.color + property alias iconName: content.iconName + property alias colorScheme: buttonColorScheme + + + padding: 0 + + hoverEnabled: true + + KColorScheme { + id: buttonColorScheme + colorGroup: button.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet: KColorScheme.Button + } + + contentItem: IconLabel { + color: buttonColorScheme.normalText + id: content + text: button.text + elide: "ElideNone" + + Behavior on color { + ColorAnimation { duration: 150 } + } + + } + + background: Item { + Rectangle { + anchors.fill: parent; + id: bg + color: buttonColorScheme.alternateBackground + + HueSaturation { + anchors.fill: bg + source: bg + saturation: hovered? 0.3: 0 + lightness: hovered? -0.04: 0 + Behavior on saturation { + NumberAnimation { + duration: 150 + } + } + Behavior on lightness { + NumberAnimation { + duration: 150 + } + } + } + } + + FocusBar { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 3 + control: button + } + } +} diff --git a/src/qml/common/IconLabel.qml b/src/qml/common/IconLabel.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/IconLabel.qml @@ -0,0 +1,44 @@ +/* + * 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.Controls 2.2 + +Label { + property string iconName: "" + property bool reserveSpaceForIcon: false + id: label + elide: Text.ElideRight + + padding: Math.ceil(0.7 * font.pixelSize) + leftPadding: (iconItem.visible || reserveSpaceForIcon? padding + iconItem.width: 0) + (label.text !== ""? padding: 0) + verticalAlignment: Text.AlignVCenter + + MonochromeIcon { + id: iconItem + visible: label.iconName != "" + color: label.color + anchors { + left: parent.left + leftMargin: label.text === ""? (label.width - width) / 2: label.padding + verticalCenter: parent.verticalCenter + } + icon: label.iconName + width: 22 + height: 22 + } +} diff --git a/src/qml/common/IconToolButton.qml b/src/qml/common/IconToolButton.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/IconToolButton.qml @@ -0,0 +1,68 @@ +/* + * 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.Controls 2.2 +import ktouch 1.0 + +ToolButton { + id: button + + property alias color: content.color + property alias iconName: content.iconName + property color backgroundColor: button.colorScheme.normalBackground + + property KColorScheme colorScheme: KColorScheme { + id: buttonColorScheme + colorGroup: button.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet: KColorScheme.Button + } + + padding: 0 + + hoverEnabled: true + + contentItem: IconLabel { + id: content + text: button.text + color: button.colorScheme.normalText + elide: "ElideNone" + } + + background: Item { + Rectangle { + anchors.fill: parent + opacity: (button.checked? 0.6: 0) + (button.activeFocus || button.hovered? 0.3: 0) + color: button.backgroundColor + Behavior on opacity { + NumberAnimation { + duration: 150 + } + } + } + + FocusBar { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 3 + control: button + } + } +} diff --git a/src/qml/common/InfoItem.qml b/src/qml/common/InfoItem.qml --- a/src/qml/common/InfoItem.qml +++ b/src/qml/common/InfoItem.qml @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 QtObject { property string title diff --git a/src/qml/common/InformationTable.qml b/src/qml/common/InformationTable.qml --- a/src/qml/common/InformationTable.qml +++ b/src/qml/common/InformationTable.qml @@ -16,9 +16,9 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Controls 2.2 +import QtQuick.Layouts 1.3 import ktouch 1.0 Item { diff --git a/src/qml/common/InlineToolbar.qml b/src/qml/common/InlineToolbar.qml --- a/src/qml/common/InlineToolbar.qml +++ b/src/qml/common/InlineToolbar.qml @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 Item { diff --git a/src/qml/common/InfoItem.qml b/src/qml/common/Label.qml copy from src/qml/common/InfoItem.qml copy to src/qml/common/Label.qml --- a/src/qml/common/InfoItem.qml +++ b/src/qml/common/Label.qml @@ -1,5 +1,5 @@ /* - * Copyright 2012 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 @@ -15,10 +15,20 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 +import QtQuick.Controls 2.2 as Controls +import ktouch 1.0 -QtObject { - property string title - property string text -} +Controls.Label { + id: control + + property alias colorScheme: colorScheme + KColorScheme { + id: colorScheme + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet: KColorScheme.Window + } + + color: colorScheme.normalText +} diff --git a/src/qml/common/LearningProgressChart.qml b/src/qml/common/LearningProgressChart.qml --- a/src/qml/common/LearningProgressChart.qml +++ b/src/qml/common/LearningProgressChart.qml @@ -15,17 +15,16 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 -import org.kde.charts 0.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 +import org.kde.charts 0.1 as Charts import ktouch 1.0 -LineChart { - id: chart +Charts.LineChart { + id: root - property Dimension accuracy: accuracyDimension - property Dimension charactersPerMinute: charactersPerMinuteDimension + property Charts.Dimension accuracy: accuracyDimension + property Charts.Dimension charactersPerMinute: charactersPerMinuteDimension pitch: 60 @@ -42,22 +41,75 @@ } dimensions: [ - Dimension { + Charts.Dimension { id: accuracyDimension dataColumn: 5 color: "#ffb12d" - minimumValue: chart.minAccuracy(model.minAccuracy) + minimumValue: root.minAccuracy(model.minAccuracy) maximumValue: 1.0 label: i18n("Accuracy") unit: "%" unitFactor: 100 }, - Dimension { + Charts.Dimension { id: charactersPerMinuteDimension dataColumn: 6 color: "#38aef4" maximumValue: Math.max(Math.ceil(model.maxCharactersTypedPerMinute / 120) * 120, 120) label: i18n("Characters per Minute") } ] + + onElemEntered: { + learningProgressPointTooltip.visualParent = elem + learningProgressPointTooltip.row = row + learningProgressPointTooltip.open() + } + + onElemExited: { + learningProgressPointTooltip.close() + } + + Balloon { + id: learningProgressPointTooltip + property int row: -1 + + function findLessonTitle(id) { + var course = model.courseFilter + if (course) { + for (var i = 0; i < course.lessonCount; i++) { + if (course.lesson(i).id === id) { + return course.lesson(i).title + } + } + } + return i18n("Unknown") + } + + InformationTable { + property list infoModel: [ + InfoItem { + title: i18nc("Statistics on lesson:", "Lesson:") + text: learningProgressPointTooltip.row !== -1? learningProgressPointTooltip.findLessonTitle(learningProgressModel.lessonId(learningProgressPointTooltip.row)): "" + }, + InfoItem { + title: i18n("Training on:") + text: learningProgressPointTooltip.row !== -1? + learningProgressModel.date(learningProgressPointTooltip.row).toLocaleString(): + "" + }, + InfoItem { + title: i18n("Accuracy:") + text: learningProgressPointTooltip.row !== -1? strFormatter.formatAccuracy(learningProgressModel.accuracy(learningProgressPointTooltip.row)): "" + }, + InfoItem { + title: i18n("Characters per Minute:") + text: learningProgressPointTooltip.row !== -1? learningProgressModel.charactersPerMinute(learningProgressPointTooltip.row): "" + } + ] + width: 250 + model: infoModel + } + } + } diff --git a/src/qml/common/ListItem.qml b/src/qml/common/ListItem.qml --- a/src/qml/common/ListItem.qml +++ b/src/qml/common/ListItem.qml @@ -16,85 +16,48 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 -import org.kde.kquickcontrolsaddons 2.0 +import QtQuick 2.9 +import QtQuick.Controls 2.2 import ktouch 1.0 -Item { +ItemDelegate { id: root - property alias title: label.text - property string iconSource + property string iconName property alias label: label - property bool isCurrent: ListView.isCurrentItem; - signal clicked - signal doubleClicked - height: 2 * content.height + property alias bg: bg + property alias reserveSpaceForIcon: label.reserveSpaceForIcon + hoverEnabled: true + padding: 0 + + KColorScheme { + id: listItemColorSchemeNormal + colorGroup: KColorScheme.Active + colorSet: KColorScheme.View + } - SystemPalette { - id: listItemPallete - colorGroup: SystemPalette.Active + KColorScheme { + id: listItemColorSchemeHighlighted + colorGroup: KColorScheme.Active + colorSet: KColorScheme.Selection } - Rectangle { + background: Rectangle { id: bg anchors.fill: parent - color: Qt.rgba(listItemPallete.highlight.r, listItemPallete.highlight.g, listItemPallete.highlight.b, 0.3) - radius: 0.2 * height - border { - width: 1 - color: listItemPallete.highlight - } - opacity: root.isCurrent || mouseArea.containsMouse? 1: 0 + color: listItemColorSchemeHighlighted.normalBackground + opacity: root.highlighted ? 1 : (root.hovered? 0.3: 0) Behavior on opacity { NumberAnimation { duration: 150 - } - } - } - - Item { - id: content - anchors { - verticalCenter: parent.verticalCenter - left: parent.left - right: parent.right - leftMargin: 5 - rightMargin: 5 - } - - height: Math.max(label.height, label.height) - - QIconItem { - id: iconItem - visible: !!root.iconSource - anchors { - left: parent.left - verticalCenter: parent.verticalCenter - } - icon: root.iconSource - width: 22 - height: 22 - } - - Label { - id: label - elide: Text.ElideRight - anchors { - left: iconItem.visible? iconItem.right: parent.left - right: parent.right - verticalCenter: parent.verticalCenter } } } - MouseArea { - id: mouseArea - anchors.fill: parent - hoverEnabled: true - onClicked: root.clicked() - onDoubleClicked: root.doubleClicked() + contentItem: IconLabel { + id: label + text: root.text + iconName: root.iconName + color: root.highlighted? listItemColorSchemeHighlighted.activeText: listItemColorSchemeNormal.normalText; } } diff --git a/src/qml/common/ListView.qml b/src/qml/common/ListView.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/ListView.qml @@ -0,0 +1,64 @@ +/* + * 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.Controls 2.2 as Controls +import ktouch 1.0 + +ListView { + id: control + + property alias colorScheme: colorScheme + + activeFocusOnTab: true + + KColorScheme { + id: colorScheme + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet: KColorScheme.View + } + + Rectangle { + anchors.fill: parent + color: colorScheme.normalBackground + z: -1 + } + + FocusBar { + anchors { + top: parent.top + left: parent.left + right: parent.right + } + height: 3 + control: control + z: -1 + } + + FocusBar { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + } + height: 3 + control: control + z: -1 + } + + Controls.ScrollBar.vertical: ScrollBar { } +} diff --git a/src/qml/common/MessageBox.qml b/src/qml/common/MessageBox.qml --- a/src/qml/common/MessageBox.qml +++ b/src/qml/common/MessageBox.qml @@ -1,6 +1,5 @@ /* - * Copyright 2012 Sebastian Gottfried - * Copyright 2015 Sebastian Gottfried + * 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 @@ -16,23 +15,23 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import org.kde.kquickcontrolsaddons 2.0 +import QtQuick 2.9 + +import "../common" Rectangle { id: item anchors.verticalCenter: parent.verticalCenter radius: 3 color: "#eee4be" - height: layout.height + 6 - width: layout.width + 6 + height: label.height + 6 + width: label.width + 6 smooth: true function showMessage(msg, iconSource) { item.state = "hidden"; label.text = msg - icon.icon = iconSource || "" + label.iconName = iconSource || "" item.state = "normal" } @@ -57,22 +56,10 @@ state: "hidden" - Row { - id: layout + IconLabel { anchors.centerIn: parent - width: icon.width + spacing + label.width - spacing: icon.valid? 3: 0 - - QIconItem { - id: icon - width: height - height: icon? label.height: 0 - } - - Label { - id: label - color: "#000000" - } + id: label + padding: 2 } states: [ diff --git a/src/qml/common/InfoItem.qml b/src/qml/common/MonochromeIcon.qml copy from src/qml/common/InfoItem.qml copy to src/qml/common/MonochromeIcon.qml --- a/src/qml/common/InfoItem.qml +++ b/src/qml/common/MonochromeIcon.qml @@ -1,5 +1,5 @@ /* - * Copyright 2012 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 @@ -15,10 +15,16 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtGraphicalEffects 1.0 -QtObject { - property string title - property string text +Icon { + property color color: "#000000" + id: icon + width: 24 + height: 24 + ColorOverlay { + anchors.fill: parent + source: icon + color: parent.color + } } - diff --git a/src/qml/common/PopupDialog.qml b/src/qml/common/PopupDialog.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/PopupDialog.qml @@ -0,0 +1,144 @@ +/* + * 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.Controls 2.2 +import QtQuick.Layouts 1.3 +import ktouch 1.0 +import QtGraphicalEffects 1.0 + +Dialog { + id: root + dim: true + + opacity: 0 + scale: 0.9 + leftMargin: Math.floor((parent.width - width) / 2) + topMargin: Math.floor((parent.height - height) / 2) + + Component.onCompleted: { + var candidate = root + while (candidate.parent) { + candidate = candidate.parent + } + if (candidate) { + root.parent = candidate + } + } + + onOpened: { + contentItem.forceActiveFocus() + } + + Item { + id: dimOverlay + parent: root.parent + width: root.parent.width + height: root.parent.height + visible: root.visible && root.dim + opacity: 0 + + ShaderEffectSource { + id: effectSource + sourceItem: root.parent.appContent + anchors.fill: parent + hideSource: false + } + + HueSaturation { + id: desaturatedBackground + source: effectSource + anchors.fill: parent + lightness: -0.3 + saturation: -0.5 + visible: false + } + + FastBlur { + anchors.fill: parent + source: desaturatedBackground + radius: 50 + } + + Rectangle { + anchors.fill: parent + color: "#55000000" + } + } + + enter: Transition { + // grow_fade_in + NumberAnimation { property: "scale"; to: 1.0; easing.type: Easing.OutQuint; duration: 220 } + NumberAnimation { property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: 150 } + NumberAnimation { target: dimOverlay; property: "opacity"; to: 1.0; easing.type: Easing.OutCubic; duration: 220 } + } + + exit: Transition { + // shrink_fade_out + NumberAnimation { property: "scale"; to: 0.9; easing.type: Easing.OutQuint; duration: 220 } + NumberAnimation { property: "opacity"; to: 0.0; easing.type: Easing.OutCubic; duration: 150 } + NumberAnimation { target: dimOverlay; property: "opacity"; to: 0.0; easing.type: Easing.OutCubic; duration: 220 } + } + + background: Rectangle { + color: dialogColorScheme.normalBackground + + KColorScheme { + id: dialogColorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.Window + } + } + + header: Rectangle { + implicitHeight: Math.max(titleLabel.implicitHeight, closeButton.implicitHeight) + 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 + + Label { + id: titleLabel + text: root.title + width: parent.width + color: toolbarColorScheme.normalText + font.bold: true + padding: font.pixelSize + Layout.fillWidth: true + } + + IconToolButton { + id: closeButton + iconName: "window-close-symbolic" + color: toolbarColorScheme.normalText + visible: root.closePolicy & Popup.CloseOnEscape + backgroundColor: toolbarColorScheme.normalBackground + activeFocusOnTab: false + Layout.preferredWidth: titleLabel.implicitHeight + Layout.preferredHeight: titleLabel.implicitHeight + onClicked: root.close() + } + } + } +} diff --git a/src/qml/common/RadioButton.qml b/src/qml/common/RadioButton.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/RadioButton.qml @@ -0,0 +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.Controls 2.2 +import ktouch 1.0 + +RadioButton { + id: control + + hoverEnabled: true + spacing: label.font.pixelSize + padding: 0 + + property alias colorScheme: colorScheme + property alias selectionColorScheme: selectionColorScheme + property alias label: label + + KColorScheme { + id: colorScheme + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet: KColorScheme.Button + } + + KColorScheme { + id: selectionColorScheme + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet: KColorScheme.Selection + } + + indicator: Rectangle { + implicitWidth: control.font.pixelSize + implicitHeight: control.font.pixelSize + x: control.leftPadding + y: parent.height / 2 - height / 2 + radius: height / 2 + + border.color: control.checked? + Qt.lighter(colorScheme.focusDecoration, control.hovered? 1.3: 1.0): + utils.alpha(colorScheme.normalText, control.hovered? 0.9: 0.5) + + color: "#00000000" + + Behavior on border.color { + ColorAnimation { + duration: 150 + } + } + + Rectangle { + anchors.fill: parent + anchors.margins: 3 + radius: height / 2 + color: Qt.lighter(colorScheme.focusDecoration, control.hovered? 1.3: 1.0) + visible: control.checked + } + } + + contentItem: Label { + id: label + text: control.text + color: control.colorScheme.normalText + horizontalAlignment: Text.AlignLeft + verticalAlignment: Text.AlignVCenter + topPadding: 3 + leftPadding: control.indicator.width + control.spacing + bottomPadding: 3 + + FocusBar { + anchors { + left: parent.left + right: parent.right + bottom: parent.bottom + leftMargin: parent.leftPadding + } + control: control + } + } +} diff --git a/src/qml/common/ScrollBar.qml b/src/qml/common/ScrollBar.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/ScrollBar.qml @@ -0,0 +1,50 @@ +/* + * 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.Controls 2.2 as Controls +import ktouch 1.0 + +Controls.ScrollBar { + id: control + + property alias colorScheme: colorScheme + + KColorScheme { + id: colorScheme + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet: KColorScheme.Window + } + + contentItem: Rectangle { + implicitWidth: control.orientation == Qt.Horizontal? 100: 6 + implicitHeight: control.orientation == Qt.Horizontal? 6: 100 + radius: 3 + color: control.pressed ? colorScheme.focusDecoration: colorScheme.alternateBackground + opacity: control.active? 1: 0.3 + Behavior on opacity { + NumberAnimation { + duration: 150 + } + } + Behavior on color { + ColorAnimation { + duration: 150 + } + } + } +} diff --git a/src/qml/common/ScrollView.qml b/src/qml/common/ScrollView.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/ScrollView.qml @@ -0,0 +1,42 @@ +/* + * 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.Controls 2.2 as Controls +import ktouch 1.0 + +Controls.ScrollView { + id: control + + Controls.ScrollBar.vertical: ScrollBar { + parent: control + x: control.mirrored ? 0 : control.width - width + y: control.topPadding + height: control.availableHeight + active: control.ScrollBar.horizontal.active + visible: size < 1 + } + + Controls.ScrollBar.horizontal: ScrollBar { + parent: control + x: control.leftPadding + y: control.height - height + width: control.availableWidth + active: control.ScrollBar.vertical.active + visible: size < 1 + } +} diff --git a/src/qml/common/SelectionGrip.qml b/src/qml/common/SelectionGrip.qml --- a/src/qml/common/SelectionGrip.qml +++ b/src/qml/common/SelectionGrip.qml @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 import ktouch 1.0 Item { diff --git a/src/qml/common/SelectionRectangle.qml b/src/qml/common/SelectionRectangle.qml --- a/src/qml/common/SelectionRectangle.qml +++ b/src/qml/common/SelectionRectangle.qml @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 import ktouch 1.0 Item diff --git a/src/qml/common/SheetDialog.qml b/src/qml/common/SheetDialog.qml deleted file mode 100644 --- a/src/qml/common/SheetDialog.qml +++ /dev/null @@ -1,109 +0,0 @@ -/* - * 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 1.3 -import QtQuick.Layouts 1.1 -import ktouch 1.0 - -Item { - id: root - - SystemPalette { - id: activePalette - colorGroup: SystemPalette.Active - } - - property alias content: contentArea.children - property int innerMargin: 20 - signal opened - signal closed - - function open() { - root.state = "open" - opened() - } - - function close() { - root.state = "closed" - closed() - } - - function isOpen() { - return root.state == "open" - } - - clip: true - visible: bg.opacity > 0 - - /* swallow all mouse events */ - MouseArea { - anchors.fill: parent - hoverEnabled: true - } - - Rectangle { - id: bg - anchors.fill: parent - color: activePalette.window - opacity: 1.0 - - Behavior on opacity { - NumberAnimation { - duration: 300 - } - } - } - - Rectangle { - id: slider - color: activePalette.window - width: parent.width - height: parent.height - - Item { - id: contentArea - anchors { - fill: parent - } - } - } - - state: "closed" - - states: [ - State { - name: "open" - PropertyChanges { target: bg; opacity: 1.0 } - PropertyChanges { target: slider; y: 0 } - }, - State { - name: "closed" - PropertyChanges { target: bg; opacity: 0 } - PropertyChanges { target: slider; y: -slider.parent.height } - } - ] - - transitions: [ - Transition { - from: "*" - to: "*" - NumberAnimation { target: slider; property: "y"; duration: 300; easing.type: Easing.InOutQuad } - } - ] -} diff --git a/src/qml/common/TextArea.qml b/src/qml/common/TextArea.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/TextArea.qml @@ -0,0 +1,57 @@ +/* + * 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.Controls 2.2 as Controls +import ktouch 1.0 + +Controls.TextArea { + id: control + + property KColorScheme colorScheme: KColorScheme { + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet:KColorScheme.View + } + + property KColorScheme selectionColorScheme: KColorScheme { + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet:KColorScheme.Selection + } + + selectByMouse: true + selectByKeyboard: true + color: control.colorScheme.normalText + selectionColor: selectionColorScheme.normalBackground + selectedTextColor: selectionColorScheme.normalText + + background: Rectangle { + color: control.colorScheme.normalBackground + anchors.fill: parent + border.width: 1 + border.color: control.activeFocus? + control.colorScheme.focusDecoration: + Qt.hsva(control.colorScheme.focusDecoration.hslHue, + 0, + control.colorScheme.focusDecoration.hslValue, + control.enabled? 0.5: 0.2) + Behavior on border.color { + ColorAnimation { duration: 150 } + } + } + + +} diff --git a/src/qml/common/TextField.qml b/src/qml/common/TextField.qml new file mode 100644 --- /dev/null +++ b/src/qml/common/TextField.qml @@ -0,0 +1,52 @@ +/* + * 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.Controls 2.2 as Controls +import ktouch 1.0 + +Controls.TextField { + id: control + + property KColorScheme colorScheme: KColorScheme { + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet:KColorScheme.View + } + + property KColorScheme selectionColorScheme: KColorScheme { + colorGroup: control.enabled? KColorScheme.Active: KColorScheme.Disabled + colorSet:KColorScheme.Selection + } + + selectByMouse: true + selectionColor: selectionColorScheme.normalBackground + selectedTextColor: selectionColorScheme.normalText + + background: Rectangle { + color: control.colorScheme.normalBackground + border.width: 1 + border.color: control.activeFocus? + control.colorScheme.focusDecoration: + Qt.hsva(control.colorScheme.focusDecoration.hslHue, + 0, + control.colorScheme.focusDecoration.hslValue, + control.enabled? 0.5: 0.2) + Behavior on border.color { + ColorAnimation { duration: 150 } + } + } +} diff --git a/src/bindings/utils.h b/src/qml/common/ToolBar.qml copy from src/bindings/utils.h copy to src/qml/common/ToolBar.qml --- a/src/bindings/utils.h +++ b/src/qml/common/ToolBar.qml @@ -1,5 +1,5 @@ /* - * Copyright 2012 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 @@ -15,24 +15,24 @@ * along with this program. If not, see . */ -#ifndef UTILS_H -#define UTILS_H +import QtQuick 2.9 +import QtQuick.Controls 2.2 as Controls +import ktouch 1.0 -#include -#include -#include +Controls.ToolBar { + id: control -class Utils : public QObject -{ - Q_OBJECT -public: - explicit Utils(QObject* parent = 0); - Q_INVOKABLE QUrl findImage(const QString &name); - Q_INVOKABLE int getMinutesOfQTime(const QTime& time); - Q_INVOKABLE int getSecondsOfQTime(const QTime& time); - Q_INVOKABLE QString uuid(); + property alias colorScheme: colorScheme + property real dimFactor: 1.3 -}; + background: Rectangle { + color: colorScheme.toolbarBackground + } - -#endif // UTILS_H + KColorScheme { + id: colorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.Complementary + property color toolbarBackground: Qt.darker(colorScheme.shade(colorScheme.hoverDecoration, KColorScheme.MidShade, colorScheme.contrast, -0.2), control.dimFactor) + } +} diff --git a/src/bindings/utils.h b/src/qml/common/ToolSeparator.qml copy from src/bindings/utils.h copy to src/qml/common/ToolSeparator.qml --- a/src/bindings/utils.h +++ b/src/qml/common/ToolSeparator.qml @@ -1,5 +1,5 @@ /* - * Copyright 2012 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 @@ -15,24 +15,27 @@ * along with this program. If not, see . */ -#ifndef UTILS_H -#define UTILS_H +import QtQuick 2.9 +import QtQuick.Controls 2.2 as Controls +import ktouch 1.0 -#include -#include -#include +Controls.ToolSeparator { + id: control -class Utils : public QObject -{ - Q_OBJECT -public: - explicit Utils(QObject* parent = 0); - Q_INVOKABLE QUrl findImage(const QString &name); - Q_INVOKABLE int getMinutesOfQTime(const QTime& time); - Q_INVOKABLE int getSecondsOfQTime(const QTime& time); - Q_INVOKABLE QString uuid(); + property alias colorScheme: colorScheme -}; + KColorScheme { + id: colorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.Complementary + } + padding: vertical? 6: 2 + topPadding: vertical? 2: 6 + bottomPadding: vertical? 2: 6 - -#endif // UTILS_H + contentItem: Rectangle { + implicitWidth: control.vertical? 1: 24 + implicitHeight: control.vertical? 24: 1 + color: control.colorScheme.normalText + } +} diff --git a/src/bindings/utils.h b/src/qml/common/ToolTip.qml copy from src/bindings/utils.h copy to src/qml/common/ToolTip.qml --- a/src/bindings/utils.h +++ b/src/qml/common/ToolTip.qml @@ -1,5 +1,5 @@ /* - * Copyright 2012 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 @@ -15,24 +15,29 @@ * along with this program. If not, see . */ -#ifndef UTILS_H -#define UTILS_H +import QtQuick 2.9 +import QtQuick.Controls 2.2 as Controls +import ktouch 1.0 -#include -#include -#include +Controls.ToolTip { + id: control -class Utils : public QObject -{ - Q_OBJECT -public: - explicit Utils(QObject* parent = 0); - Q_INVOKABLE QUrl findImage(const QString &name); - Q_INVOKABLE int getMinutesOfQTime(const QTime& time); - Q_INVOKABLE int getSecondsOfQTime(const QTime& time); - Q_INVOKABLE QString uuid(); + property alias colorScheme: colorScheme -}; + KColorScheme { + id: colorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.Complementary + } + contentItem: Text { + text: control.text + font: control.font + color: colorScheme.normalText + } -#endif // UTILS_H + background: Rectangle { + radius: height / 6 + color: control.colorScheme.normalBackground + } +} diff --git a/src/qml/homescreen/CourseDescriptionItem.qml b/src/qml/homescreen/CourseDescriptionItem.qml --- a/src/qml/homescreen/CourseDescriptionItem.qml +++ b/src/qml/homescreen/CourseDescriptionItem.qml @@ -16,69 +16,35 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 import ktouch 1.0 -Rectangle { +import "../common" + +Collapsable { id: root property string description - property bool active: false - - SystemPalette { - id: palette - colorGroup: SystemPalette.Active - } - - height: active || content.opacity > 0? content.height: 0 - visible: height > 0 - color: palette.base - - Behavior on height { - NumberAnimation { - duration: 150 - easing.type: Easing.InOutQuad - } - } onDescriptionChanged: { - if (content.opacity === 0) { + if (contentItem.opacity === 0) { descriptionLabel.text = description } else { - content.needsUpdate = true + swapContent() } } - Item { - id: content - - property bool needsUpdate: false - - width: parent.width - height: descriptionLabel.height + 6 - opacity: root.active && !content.needsUpdate && root.height === root.childrenRect.height? 1: 0 - - Behavior on opacity { - NumberAnimation { - duration: 150 - } - } - - onOpacityChanged: { - if (needsUpdate && opacity === 0) { - descriptionLabel.text = root.description - needsUpdate = false - } - } + onContentReadyForSwap: { + descriptionLabel.text = description + } - Label { - id: descriptionLabel - anchors.centerIn: parent - width: parent.width - 10 - } + Label { + id: descriptionLabel + leftPadding: 20 + rightPadding: 20 + topPadding: 10 + bottomPadding: 10 } } diff --git a/src/qml/homescreen/CoursePage.qml b/src/qml/homescreen/CoursePage.qml deleted file mode 100644 --- a/src/qml/homescreen/CoursePage.qml +++ /dev/null @@ -1,144 +0,0 @@ -/* - * 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 1.3 -import QtQuick.Layouts 1.1 -import ktouch 1.0 - -Item { - id: root - - property Profile profile - property DataIndexCourse dataIndexCourse - property KeyboardLayout keyboardLayout - property string keyboardLayoutName - signal lessonSelected(variant course, variant lesson) - - property Course course: lessonSelector.visible? lessonSelector.course: customLessonSelector.course - - function getCourse() { - return !!dataIndexCourse? lessonSelector.course: customLessonSelector.course - } - - width: parent.width - height: parent.height - visible: false - opacity: 0 - - function showImmediately() { - root.x = 0 - root.opacity = 1 - root.visible = 1 - } - - function show(direction) { - appearAnimation.stop() - disappearAnimation.stop() - appearAnimation.direction = direction - appearAnimation.start() - } - - function hide(direction) { - appearAnimation.stop() - disappearAnimation.stop() - disappearAnimation.direction = direction - disappearAnimation.start() - } - - - QtObject { - id: priv - property int duration: 250 - } - - LessonSelector { - id: lessonSelector - - anchors.fill: parent - visible: !!dataIndexCourse - - profile: root.profile - dataIndexCourse: root.dataIndexCourse - onLessonSelected: root.lessonSelected(course, lesson) - } - - CustomLessonSelector { - id: customLessonSelector - anchors.fill: parent - visible: !root.dataIndexCourse - profile: root.profile - keyboardLayout: root.keyboardLayout - keyboardLayoutName: root.keyboardLayoutName - onLessonSelected: root.lessonSelected(course, lesson) - } - - SequentialAnimation { - id: appearAnimation - property int direction: Item.Left - PropertyAction { - target: root - property: "visible" - value: true - } - ParallelAnimation { - NumberAnimation { - target: root - property: "x" - from: appearAnimation.direction === Item.Left? root.width: -root.width - to: 0 - easing.type: Easing.InQuad - duration: priv.duration - } - NumberAnimation { - target: root - property: "opacity" - from: 0 - to: 1 - easing.type: Easing.InQuad - duration: priv.duration - } - } - } - - SequentialAnimation { - id: disappearAnimation - property int direction: Item.Left - ParallelAnimation { - NumberAnimation { - target: root - property: "x" - to: disappearAnimation.direction === Item.Left? -root.width: root.width - easing.type: Easing.InQuad - duration: priv.duration - } - NumberAnimation { - target: root - property: "opacity" - to: 0 - easing.type: Easing.InQuad - duration: priv.duration - } - } - PropertyAction { - target: root - property: "visible" - value: false - } - } -} diff --git a/src/qml/homescreen/CourseSelector.qml b/src/qml/homescreen/CourseSelector.qml --- a/src/qml/homescreen/CourseSelector.qml +++ b/src/qml/homescreen/CourseSelector.qml @@ -16,203 +16,177 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 +import QtQuick.Controls 2.2 as Controls import ktouch 1.0 -Item { +import "../common" + +FocusScope { id: root - property CategorizedResourceSortFilterProxyModel courseModel property Profile profile - property KeyboardLayout keyboardLayout - property string keyboardLayoutName + 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; - if (courseId === "custom_lessons") { - selectCourse(courseRepeater.count, true) - return - } - - for (var i = 0; i < courseModel.rowCount(); i++) { - var dataIndexCourse = courseModel.data(courseModel.index(i, 0), ResourceModel.DataRole); + // 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) { - selectCourse(i, true) + root.selectedCourse = dataIndexCourse return } } - selectCourse(0, true) - } - - function selectCourse(index, automaticSelection) { - if (index === priv.currentIndex) { - 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; } - var direction = index > priv.currentIndex? Item.Left: Item.Right - var dataIndexCourse = index < courseModel.rowCount()? - courseModel.data(courseModel.index(index, 0), ResourceModel.DataRole): - null; - var targetPage = automaticSelection? coursePageContainer.activePage: coursePageContainer.inactivePage + // finally just select the first course + if (allCoursesModel.rowCount() > 0) { + root.selectedCourse = allCoursesModel.data(allCoursesModel.index(0, 0), ResourceModel.DataRole); + } + } - priv.currentIndex = index; - targetPage.dataIndexCourse = dataIndexCourse + onSelectedCourseChanged: { + root.selectedKeyboardLayoutName = root.selectedCourse.keyboardLayoutName; - if (!automaticSelection) { - coursePageContainer.inactivePage = coursePageContainer.activePage - coursePageContainer.activePage = targetPage - coursePageContainer.inactivePage.hide(direction) - coursePageContainer.activePage.show(direction) + for (var i = 0; i < ktouch.globalDataIndex.keyboardLayoutCount; i++) + { + var dataIndexLayout = ktouch.globalDataIndex.keyboardLayout(i) - saveLastUsedCourse(dataIndexCourse? dataIndexCourse.id: "custom_lessons") + if (dataIndexLayout.name === root.selectedKeyboardLayoutName) { + root.selectedKeyboardLayout = dataIndexLayout; + return + } } + + root.selectedKeyboardLayout = null; } - function saveLastUsedCourse(courseId) { - profile.lastUsedCourseId = courseId; - profileDataAccess.updateProfile(profileDataAccess.indexOfProfile(profile)); + function saveLastUsedCourse(course) { + if (profile.lastUsedCourseId != course.id) { + profile.lastUsedCourseId = course.id; + profileDataAccess.updateProfile(profileDataAccess.indexOfProfile(profile)); + } } onProfileChanged: selectLastUsedCourse() - Connections { - target: courseModel + ResourceModel { + id: resourceModel + dataIndex: ktouch.globalDataIndex onRowsRemoved: { - nextButton.visible = previousButton.visible = courseModel.rowCount() > 1 - priv.currentIndex = -1 selectLastUsedCourse() } onRowsInserted: { - nextButton.visible = previousButton.visible = courseModel.rowCount() > 1 - priv.currentIndex = -1 selectLastUsedCourse() } } - QtObject { - id: priv - property int currentIndex: -1 + CategorizedResourceSortFilterProxyModel { + id: allCoursesModel + resourceModel: resourceModel + resourceTypeFilter: ResourceModel.CourseItem } - SystemPalette { - id: palette - colorGroup: SystemPalette.Active - } - ColumnLayout { - anchors.fill: parent - spacing: 0 - - Rectangle { - id: head - Layout.fillWidth: true - height: Math.ceil(Math.max(courseTitleLabel.height, courseDescriptionButton.height) + 6) - color: palette.base - - RowLayout { - anchors { - fill: parent - leftMargin: 5 - rightMargin: 5 - topMargin: 3 - bottomMargin: 3 - } + CategorizedResourceSortFilterProxyModel { + id: coursesForCurrentKeyboardLayoutModel + resourceModel: resourceModel + resourceTypeFilter: ResourceModel.CourseItem + keyboardLayoutNameFilter: root.currentKeyboardLayoutName + } - Label { - anchors.verticalCenter: parent.verticalCenter - id: courseTitleLabel - font.pointSize: 1.5 * Qt.font({'family': 'sansserif'}).pointSize - text: coursePageContainer.activePage.course.title - } + CategorizedResourceSortFilterProxyModel { + id: currentKeyboardLayoutsModel + resourceModel: resourceModel + resourceTypeFilter: ResourceModel.KeyboardLayoutItem + keyboardLayoutNameFilter: root.currentKeyboardLayoutName + } - Item { - id: smallSpacer - height: parent.height - width: 3 - } + CategorizedResourceSortFilterProxyModel { + id: otherKeyboardLayoutsModel + resourceModel: resourceModel + resourceTypeFilter: ResourceModel.KeyboardLayoutItem + keyboardLayoutNameFilter: root.currentKeyboardLayoutName + invertedKeyboardLayoutNameFilter: true + } - ToolButton { - id: courseDescriptionButton - anchors.verticalCenter: parent.verticalCenter - iconName: "dialog-information" - checkable: true - } + KColorScheme { + id: courseSelectorColorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.View + } - Item { - Layout.fillWidth: true - Layout.fillHeight: true - } + Rectangle { + id: bg + anchors.fill: parent + color: courseSelectorColorScheme.normalBackground + } - ToolButton { - id: previousButton - anchors.verticalCenter: parent.verticalCenter - iconName: "arrow-left" - enabled: priv.currentIndex > 0 - onClicked: { - var newIndex = priv.currentIndex - 1 - root.selectCourse(newIndex, false) - } - } + Flickable { + clip: true + anchors.fill: parent + contentWidth: width + contentHeight: content.height + Column { + id: content + width: parent.width - ToolButton { - id: nextButton - iconName: "arrow-right" - enabled: priv.currentIndex < courseModel.rowCount() - onClicked: { - var newIndex = (priv.currentIndex + 1) % (courseModel.rowCount() + 1) - root.selectCourse(newIndex, false) - } + CourseSelectorKeyboardLayoutList { + 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) } } - } - Item { - Layout.fillWidth: true - Layout.minimumHeight: courseDescriptionItem.height - Layout.maximumHeight: courseDescriptionItem.height - CourseDescriptionItem { - id: courseDescriptionItem - active: courseDescriptionButton.checked - description: coursePageContainer.activePage.course.description + CourseSelectorKeyboardLayoutList { width: parent.width + title: i18n('Other Courses') + model: otherKeyboardLayoutsModel + resourceModel: resourceModel + colorScheme: courseSelectorColorScheme + selectedKeyboardLayoutName: root.selectedKeyboardLayoutName + selectedCourse: root.selectedCourse + onCourseSelected: { + root.selectedCourse = course + root.saveLastUsedCourse(course) + } } } - Item { - id: coursePageContainer - property CoursePage activePage: page0 - property CoursePage inactivePage: page1 - - Layout.fillWidth: true - Layout.fillHeight: true - - CoursePage { - id: page0 - profile: root.profile - keyboardLayout: root.keyboardLayout - keyboardLayoutName: root.keyboardLayoutName - onLessonSelected: root.lessonSelected(course, lesson) - Component.onCompleted: page0.showImmediately() - } - - CoursePage { - id: page1 - profile: root.profile - keyboardLayout: root.keyboardLayout - keyboardLayoutName: root.keyboardLayoutName - onLessonSelected: root.lessonSelected(course, lesson) - } - } + Controls.ScrollBar.vertical: ScrollBar { } } + + + + + + } diff --git a/src/qml/homescreen/CourseSelectorKeyboardLayoutItem.qml b/src/qml/homescreen/CourseSelectorKeyboardLayoutItem.qml new file mode 100644 --- /dev/null +++ b/src/qml/homescreen/CourseSelectorKeyboardLayoutItem.qml @@ -0,0 +1,116 @@ +/* + * 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 ktouch 1.0 + +import "../common" + +Column { + id: root + property string name + property alias title: keyboardLayoutItem.text + property ResourceModel resourceModel + property string selectedKeyboardLayoutName + property DataIndexCourse selectedCourse + + signal courseSelected(DataIndexCourse course) + + height: keyboardLayoutItem.height + (loader.active? loader.height: 0) + clip: true + + onSelectedKeyboardLayoutNameChanged: { + if (selectedKeyboardLayoutName == root.name) { + loader.active = true + } + } + + CategorizedResourceSortFilterProxyModel { + id: courseModel + resourceModel: root.resourceModel + resourceTypeFilter: ResourceModel.CourseItem + keyboardLayoutNameFilter: loader.keyboardLayoutNameFilter + } + + ListItem { + id: keyboardLayoutItem + iconName: "input-keyboard" + width: parent.width + onClicked: { + loader.active = !loader.active + if (loader.active) { + if (courseModel.rowCount()) { + courseSelected(courseModel.data(courseModel.index(0, 0), ResourceModel.DataRole)) + } + } + } + } + + Loader { + id: loader + width: parent.width + active: false + property string keyboardLayoutNameFilter: root.name + sourceComponent: Component { + id: courseSelectionComponent + + Column { + Repeater { + id: courseRepeater + model: courseModel + ListItem { + text: dataRole.title + width: parent.width + reserveSpaceForIcon: true + highlighted: root.selectedCourse == dataRole + onClicked: { + courseSelected(dataRole) + } + } + } + ListItem { + DataIndexCourse { + id: customLessonsCourse + title: i18n("Custom Lessons") + keyboardLayoutName: root.name + Component.onCompleted: { + id = "custom_lessons" + } + } + + text: customLessonsCourse.title + id: ownLessonsItem + reserveSpaceForIcon: true + width: parent.width + highlighted: root.selectedCourse == customLessonsCourse + onClicked: { + courseSelected(customLessonsCourse) + } + } + } + } + } + + Behavior on height { + NumberAnimation { + duration: 150 + easing.type: Easing.InOutQuad + } + } +} + diff --git a/src/qml/homescreen/CourseSelectorKeyboardLayoutList.qml b/src/qml/homescreen/CourseSelectorKeyboardLayoutList.qml new file mode 100644 --- /dev/null +++ b/src/qml/homescreen/CourseSelectorKeyboardLayoutList.qml @@ -0,0 +1,57 @@ +/* + * 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 ktouch 1.0 +import '../common' + +Column { + id: root + property alias title: header.text + property CategorizedResourceSortFilterProxyModel model + property ResourceModel resourceModel + property string selectedKeyboardLayoutName + property DataIndexCourse selectedCourse: null + property KColorScheme colorScheme + signal courseSelected(DataIndexCourse course) + + ListItem { + id: header + width: parent.width + font.bold: true + bg.color: colorScheme.alternateBackground + bg.opacity: 1 + label.opacity: 0.7 + } + + Repeater { + id: repeater + model: root.model + CourseSelectorKeyboardLayoutItem { + width: parent.width + name: keyboardLayoutName + title: display + resourceModel: root.resourceModel + selectedKeyboardLayoutName: root.selectedKeyboardLayoutName + selectedCourse: root.selectedCourse + onCourseSelected: { + root.courseSelected(course) + } + } + } + +} diff --git a/src/qml/homescreen/CustomLessonSelector.qml b/src/qml/homescreen/CustomLessonSelector.qml deleted file mode 100644 --- a/src/qml/homescreen/CustomLessonSelector.qml +++ /dev/null @@ -1,232 +0,0 @@ -/* - * Copyright 2013 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 1.3 -import QtQuick.Layouts 1.1 -import ktouch 1.0 - -import "../common" - -Item { - id: root - property Profile profile - property KeyboardLayout keyboardLayout - property string keyboardLayoutName - property alias course: courseItem - signal lessonSelected(variant course, variant lesson) - - function update() { - if (!profile) - return - profileDataAccess.loadCustomLessons(profile, keyboardLayoutName, courseItem) - } - - QtObject { - id: internal - property variant deletedLessons: [] - } - - function selectLastLesson() { - if (!course.isValid) - return; - 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) { - lessonList.currentIndex = index - break - } - } - } - } - - function createNewLesson() { - tmpLesson.id = utils.uuid() - tmpLesson.title = "" - tmpLesson.text = "" - if (ktouch.showCustomLessonDialog(tmpLesson, root.keyboardLayout)) { - profileDataAccess.storeCustomLesson(tmpLesson, root.profile, keyboardLayoutName) - update() - lessonList.currentIndex = lessonList.count - 2 - } - } - - function editLesson() { - tmpLesson.copyFrom(base.selectedLesson) - if (ktouch.showCustomLessonDialog(tmpLesson, root.keyboardLayout)) { - profileDataAccess.storeCustomLesson(tmpLesson, root.profile, keyboardLayoutName) - update() - } - } - - function deleteLesson() { - var deletedLessons = internal.deletedLessons - var lesson = Qt.createQmlObject("import ktouch 1.0; Lesson{}", internal, "lesson") - lesson.copyFrom(base.selectedLesson) - deletedLessons.push(lesson) - base.selectedLesson = null - profileDataAccess.deleteCustomLesson(lesson.id) - update() - internal.deletedLessons = deletedLessons - } - - function undoLessonDeletion() { - var deletedLessons = internal.deletedLessons - var lesson = deletedLessons.pop() - internal.deletedLessons = deletedLessons - profileDataAccess.storeCustomLesson(lesson, root.profile, keyboardLayoutName) - update() - } - - function confirmLessonDeletion() { - var deletedLessons = internal.deletedLessons - var lesson = deletedLessons.pop() - internal.deletedLessons = deletedLessons - } - - function updateSelectedLesson() { - if (lessonList.currentIndex !== -1) { - base.selectedLesson = lessonList.currentItem.lesson - } - else { - base.selectedLesson = null - } - } - - onProfileChanged: update() - onKeyboardLayoutNameChanged: update() - - Course { - id: courseItem - } - - Lesson { - id: tmpLesson - } - - LessonSelectorBase { - id: base - - anchors.fill: parent - - list: ScrollView { - width: 500 - height: 500 - ListView { - id: lessonList - anchors.fill: parent - - model: course.isValid? course.lessonCount + 1: 0 - - clip: true - - delegate: ListItem { - property Lesson lesson: index < course.lessonCount? course.lesson(index): null - property bool isNewButton: index == course.lessonCount - width: lessonList.width - onClicked: { - lessonList.currentIndex = index - if (isNewButton) { - createNewLesson() - } - } - onDoubleClicked: { - if (!isNewButton) { - lessonSelected(course, lesson) - } - } - title: isNewButton? i18n("Create New Custom Lesson"): (lesson? lesson.title: "") - iconSource: isNewButton? "list-add": "" - label.font.italic: isNewButton - } - - onCurrentIndexChanged: updateSelectedLesson() - - onModelChanged: selectLastLesson() - } - } - - InlineToolbar { - id: lessonToolbar - parent: base.previewArea - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - topMargin: 5 - } - opacity: base.selectedLesson !== null? 1: 0 - content: [ - ToolButton { - iconName: "document-edit" - text: i18n("Edit") - onClicked: editLesson() - }, - ToolButton { - iconName: "edit-delete" - text: i18n("Delete") - onClicked: deleteLesson() - } - ] - } - - InlineToolbar { - id: undoToolbar - property Lesson lastDeletedLesson: internal.deletedLessons.length > 0? internal.deletedLessons[internal.deletedLessons.length - 1]: null - property string lastDeletedLessonTitle: "" - onLastDeletedLessonChanged: { - if (!!lastDeletedLesson) { - lastDeletedLessonTitle = lastDeletedLesson.title - } - } - - parent: screen - color: "#eee4be" - anchors { - top: parent.top - horizontalCenter: parent.horizontalCenter - topMargin: 15 - } - opacity: !!lastDeletedLesson? 1: 0 - content: [ - Label { - text: i18n("'%1' deleted.", undoToolbar.lastDeletedLessonTitle) - anchors.verticalCenter: parent.verticalCenter - }, - Item { - width: 5 - height: 1 - }, - ToolButton { - iconName: "edit-undo" - text: i18n("Undo") - onClicked: undoLessonDeletion() - }, - ToolButton { - iconName: "dialog-close" - text: i18n("Dismiss") - onClicked: confirmLessonDeletion() - } - ] - } - - selectedLesson: null - selectedLessonLocked: false - onStartButtonClicked: lessonSelected(course, lessonList.currentItem.lesson) - } -} diff --git a/src/qml/homescreen/HomeScreen.qml b/src/qml/homescreen/HomeScreen.qml --- a/src/qml/homescreen/HomeScreen.qml +++ b/src/qml/homescreen/HomeScreen.qml @@ -16,27 +16,22 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 +import QtGraphicalEffects 1.0 import ktouch 1.0 import "../common" FocusScope { id: screen - property CategorizedResourceSortFilterProxyModel courseModel - property KeyboardLayout keyboardLayout - property string keyboardLayoutName + property KeyboardLayout selectedKeyboardLayout: KeyboardLayout {} + property string activeKeyboardLayoutName signal lessonSelected(variant course, variant lesson, variant profile) - QtObject { - id: d - - property Profile profile - property int profileCount: profileDataAccess.profileCount - + Connections { + target: profileDataAccess onProfileCountChanged: findCurrentProfile() } @@ -46,128 +41,107 @@ } function findCurrentProfile() { - d.profile = null + profileComboBox.profile = null var lastProfileId = preferences.lastUsedProfileId for (var i = 0; i < profileDataAccess.profileCount; i++) { var profile = profileDataAccess.profile(i) if (profile.id === lastProfileId) { - d.profile = profile + profileComboBox.profile = profile return; } } if (profileDataAccess.profileCount > 0) { - d.profile = profileDataAccess.profile(0) - preferences.lastUsedProfileId = d.profile.id + profileComboBox.profile = profileDataAccess.profile(0) + preferences.lastUsedProfileId = profileComboBox.profile.id preferences.writeConfig() } } - function switchToProfile(profile) { - d.profile = profile + function safeLastUsedProfile(profile) { preferences.lastUsedProfileId = profile.id preferences.writeConfig() } - ColumnLayout { + RowLayout { anchors.fill: parent spacing: 0 - ToolBar { - visible: courseSelector.opacity > 0 - id: header - Layout.fillWidth: true - - RowLayout { - anchors.fill: parent - anchors.leftMargin: 3 - anchors.rightMargin: 3 - spacing: 5 - - Button { - // TODO: Find a better control here which supports both an icon and a label - id: profileButton - iconName: "user-identity" - text: d.profile !== null? d.profile.name: "" - onClicked: { - if (checked) { - profileSelectorSheet.open() - } - else { - profileSelectorSheet.close() - } - } - checkable: true - } - - Item { - Layout.fillWidth: true - } - - ToolButton { - id: configureButton - iconName: "configure" - onClicked: { - var position = mapToItem(null, 0, height) - ktouch.showMenu(position.x, position.y) - } - } - } - } - Item { - id: content - Layout.fillWidth: true + id: navigationArea + z: 2 + Layout.preferredWidth: 300 Layout.fillHeight: true - CourseSelector { - id: courseSelector - opacity: 1 - initialProfileForm.opacity - courseModel: screen.courseModel - profile: d.profile - keyboardLayout: screen.keyboardLayout - keyboardLayoutName: screen.keyboardLayoutName - anchors.fill: parent - onLessonSelected: screen.lessonSelected(course, lesson, d.profile) + DropShadow { + anchors.fill: navigationAreaLayout + source: navigationAreaLayout + samples: 16 + horizontalOffset: 0 + verticalOffset: 0 } - InitialProfileForm { - id: initialProfileForm - opacity: profileDataAccess.profileCount == 0? 1: 0 + ColumnLayout { + id: navigationAreaLayout anchors.fill: parent - anchors.margins: 5 + spacing: 0 - Behavior on opacity { - NumberAnimation { - duration: screen.visible? 500: 0 - easing.type: Easing.InOutCubic - } - } - } + ToolBar { + id: header - SheetDialog { - id: profileSelectorSheet - anchors.fill: parent - onOpened: { - if (d.profile) { - var index = profileDataAccess.indexOfProfile(d.profile) - profileSelector.selectProfile(index) + Layout.fillWidth: true + dimFactor: 1.3 + + RowLayout { + anchors.fill: parent + spacing: 5 + + + ProfileComboBox { + id: profileComboBox + colorScheme: header.colorScheme + manageProfileButtonBgColor: header.colorScheme.toolbarBackground + Layout.fillHeight: true + Layout.preferredWidth: 300 + Layout.fillWidth: true + onActivated: { + safeLastUsedProfile(profile) + } + } } } - onClosed: { - profileButton.checked = false; - } - content: ProfileSelector { - id: profileSelector - anchors.fill: parent - onProfileChosen: { - screen.switchToProfile(profile) - profileSelectorSheet.close() + + CourseSelector { + id: courseSelector + Layout.fillHeight: true + Layout.fillWidth: true + profile: profileComboBox.profile + currentKeyboardLayoutName: screen.activeKeyboardLayoutName + onSelectedKeyboardLayoutChanged: { + dataAccess.loadKeyboardLayout(courseSelector.selectedKeyboardLayout, screen.selectedKeyboardLayout) } } + } } + + LessonSelector { + Layout.fillHeight: true + Layout.fillWidth: true + profile: profileComboBox.profile + selectedKeyboardLayout: screen.selectedKeyboardLayout + activeKeyboardLayoutName: screen.activeKeyboardLayoutName + dataIndexCourse: courseSelector.selectedCourse + onLessonSelected: screen.lessonSelected(course, lesson, profileComboBox.profile) + z: 1 + focus: true + } + } + + InitialProfileDialog { + id: initialProfileForm + visible: profileDataAccess.profileCount == 0 } } diff --git a/src/qml/homescreen/InitialProfileForm.qml b/src/qml/homescreen/InitialProfileDialog.qml rename from src/qml/homescreen/InitialProfileForm.qml rename to src/qml/homescreen/InitialProfileDialog.qml --- a/src/qml/homescreen/InitialProfileForm.qml +++ b/src/qml/homescreen/InitialProfileDialog.qml @@ -16,41 +16,31 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 import ktouch 1.0 -FocusScope { +import '../common' +PopupDialog { id: root + modal: true + title: i18n("Welcome to Typewriting Trainer") + closePolicy: PopupDialog.NoAutoClose + padding: 20 function save() { var profile = profileDataAccess.createProfile() profile.name = form.name profile.skillLevel = form.skillLevel profileDataAccess.addProfile(profile) + close() } - GroupBox { - id: frame - anchors.centerIn: parent - title: i18n("Welcome to Typewriting Trainer") - width: form.width + 150 - height: form.height + 80 - - Item { - id: content - anchors.fill: parent - - ProfileForm { - id: form - width: 400 - anchors.centerIn: parent - doneButtonIconSource: "go-next-view" - doneButtonText: i18n("Start Training") - onDone: save() - } - } + contentItem: ProfileForm { + id: form + doneButtonIconSource: "go-next-view" + doneButtonText: i18n("Start Training") + onDone: save() } } diff --git a/src/qml/homescreen/KeyboardLayoutMismatchMessage.qml b/src/qml/homescreen/KeyboardLayoutMismatchMessage.qml new file mode 100644 --- /dev/null +++ b/src/qml/homescreen/KeyboardLayoutMismatchMessage.qml @@ -0,0 +1,81 @@ +/* + * 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 + + 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.") + } + 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 or 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/LessonDeletedMessage.qml b/src/qml/homescreen/LessonDeletedMessage.qml new file mode 100644 --- /dev/null +++ b/src/qml/homescreen/LessonDeletedMessage.qml @@ -0,0 +1,80 @@ +/* + * 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 + + collapsed: true + + property Lesson deletedLesson: Lesson {} + signal undeleteRequested() + property bool completed: false + + onVisibleChanged: { + if (!visible && completed) { + destroy(); + } + } + + Component.onCompleted: { + root.collapsed = false; + root.completed = true; + } + + Item { + implicitWidth: root.width + implicitHeight: layout.implicitHeight + 20 + + RowLayout { + id: layout + width: root.width - 40 + anchors.centerIn: parent + + Label { + text: root.deletedLesson.title? + i18n("Lesson %1 deleted.", root.deletedLesson.title): + i18n("Lesson without title deleted.") + wrapMode: Text.Wrap + Layout.fillWidth: true + } + + IconButton { + iconName: "edit-undo" + text: i18n("Undo") + onClicked: { + root.undeleteRequested(); + root.collapsed = true; + } + } + + AutoTriggerButton { + id: okButton + iconName: "dialog-ok" + text: i18n("Confirm") + onClicked: { + root.collapsed = true; + } + } + } + } +} diff --git a/src/qml/homescreen/LessonEditorDialog.qml b/src/qml/homescreen/LessonEditorDialog.qml new file mode 100644 --- /dev/null +++ b/src/qml/homescreen/LessonEditorDialog.qml @@ -0,0 +1,109 @@ +/* + * 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' + +PopupDialog { + id: root + + property Lesson lesson: Lesson {} + property KeyboardLayout keyboardLayout: KeyboardLayout {} + property Profile profile: Profile {} + + 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? root.lesson.title: "" + onTextChanged: { + if (root.lesson) { + 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? root.lesson.text: "" + onTextChanged: { + if (root.lesson) { + root.lesson.text = text + } + } + placeholderText: i18n("Lesson text") + font.family: "monospace" + + LessonTextHighlighter { + document: lessonTextArea.textDocument + allowedCharacters: root.visible? keyboardLayout.allCharacters(): "" + } + } + } + } + + footer: IconButton { + text: i18n("Done") + iconName: "dialog-ok" + bgColor: colorScheme.positiveBackground + onClicked: { + root.close() + } + + } +} diff --git a/src/qml/homescreen/LessonLockedNotice.qml b/src/qml/homescreen/LessonLockedNotice.qml --- a/src/qml/homescreen/LessonLockedNotice.qml +++ b/src/qml/homescreen/LessonLockedNotice.qml @@ -16,52 +16,25 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 import QtGraphicalEffects 1.0 -import org.kde.kquickcontrolsaddons 2.0 import ktouch 1.0 +import '../common' Item { id: root - width: content.width + 40 - height: content.height + 40 + width: content.width + height: content.height - property alias blurSource: effectSource.sourceItem + property color glowColor: "#ffffff" - SystemPalette { + KColorScheme { id: palette - colorGroup: SystemPalette.Active - } - - ShaderEffectSource { - id: effectSource - anchors.fill: parent - hideSource: false - sourceRect: Qt.rect(root.x, root.y, root.width, root.height) - } - - FastBlur { - anchors.fill: parent - source: effectSource - radius: 25 - } - - Rectangle { - id: background - anchors.fill: parent - color: palette.base - opacity: 0.3 - radius: 15 - } - - Behavior on opacity { - NumberAnimation { - duration: 200 - } + colorGroup: KColorScheme.Active + colorSet: KColorScheme.Window } Column { @@ -71,21 +44,36 @@ width: Math.max(icon.width, text.width) spacing: 10 - QIconItem { + Icon { id: icon anchors.horizontalCenter: parent.horizontalCenter icon: "object-locked" width: 128 height: 128 } - Label { - id: text - text: i18n("Complete Previous Lessons to Unlock") + Rectangle { anchors.horizontalCenter: parent.horizontalCenter - horizontalAlignment: Text.AlignHCenter - font.weight: Font.Bold - wrapMode: Text.Wrap + width: text.width + 2 * text.font.pixelSize + height: text.height + text.font.pixelSize + radius: text.font.pixelSize + color: palette.neutralBackground + + Label { + anchors.centerIn: parent + id: text + text: i18n("Complete Previous Lessons to Unlock") + horizontalAlignment: Text.AlignHCenter + font.weight: Font.Bold + wrapMode: Text.Wrap + } } } + + Glow { + anchors.fill: content + source: content + color: root.glowColor + samples: 25 + } } diff --git a/src/qml/homescreen/LessonPreview.qml b/src/qml/homescreen/LessonPreview.qml deleted file mode 100644 --- a/src/qml/homescreen/LessonPreview.qml +++ /dev/null @@ -1,128 +0,0 @@ -/* - * Copyright 2013 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 ktouch 1.0 - -Item { - id: item - - property Lesson lesson - property int margin: 30 - - onLessonChanged: swap() - - Connections { - target: lesson - onTextChanged: swap() - onTitleChanged: swap() - } - - function swap() { - if (!lesson) - return - swapPreviewAnimation.stop() - swapPreviewAnimation.from = sheet.lessonPainter == lessonPainter1? lessonPainter1: lessonPainter2 - swapPreviewAnimation.to = sheet.lessonPainter == lessonPainter1? lessonPainter2: lessonPainter1 - swapPreviewAnimation.start() - } - - Rectangle { - id: sheet - - property LessonPainter lessonPainter: lessonPainter1 - - anchors.centerIn: parent - width: lessonPainter.width - height: lessonPainter.height - opacity: lesson? 1: 0 - - Behavior on width { - enabled: swapPreviewAnimation.running - NumberAnimation { - duration: sheet.opacity == 1? 100: 0 - } - } - - Behavior on height { - enabled: swapPreviewAnimation.running - NumberAnimation { - duration: sheet.opacity == 1? 100: 0 - } - } - - Behavior on opacity { - NumberAnimation { - duration: 200 - } - } - - border { - width: 1 - color: "#000" - } - - LessonPainter { - id: lessonPainter1 - opacity: 1 - maximumWidth: item.width - maximumHeight: item.height - lesson: lesson1 - - Lesson { - id: lesson1 - } - } - - LessonPainter { - id: lessonPainter2 - maximumWidth: item.width - maximumHeight: item.height - opacity: 0 - lesson: lesson2 - - Lesson { - id: lesson2 - } - } - } - - SequentialAnimation { - id: swapPreviewAnimation - property LessonPainter from: lessonPainter1 - property LessonPainter to: lessonPainter2 - NumberAnimation { - target: swapPreviewAnimation.from - property: "opacity" - to: 0 - duration: sheet.opacity == 1? 100: 0 - } - ScriptAction { - script: { - swapPreviewAnimation.to.lesson.copyFrom(lesson) - sheet.lessonPainter = swapPreviewAnimation.to - } - } - PauseAnimation { duration: sheet.opacity == 1? 100: 0 } - NumberAnimation { - target: swapPreviewAnimation.to - property: "opacity" - to: 1 - duration: sheet.opacity == 1? 100: 0 - } - } -} diff --git a/src/qml/homescreen/LessonSelector.qml b/src/qml/homescreen/LessonSelector.qml --- a/src/qml/homescreen/LessonSelector.qml +++ b/src/qml/homescreen/LessonSelector.qml @@ -16,109 +16,376 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 import ktouch 1.0 +import QtGraphicalEffects 1.0 import "../common" -Item { - id: item +FocusScope { + id: root property Profile profile property DataIndexCourse dataIndexCourse + property KeyboardLayout selectedKeyboardLayout + property string activeKeyboardLayoutName property alias course: courseItem - signal lessonSelected(variant course, variant lesson) + property Lesson selectedLesson: null + signal lessonSelected(Course course, Lesson lesson) function update() { if (!course.isValid) return; if (!profile) return; + course.updateLastUnlockedLessonIndex(); selectLastLesson() - enableUnlockedLessons() } 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) { - lessonList.currentIndex = index; - break; + root.selectedLesson = course.lesson(index) + content.currentIndex = index + return } } } - } - - function enableUnlockedLessons() { - if (profile.skillLevel === Profile.Advanced) { - lessonList.lastUnlockedIndex = course.lessonCount - 1; - return; - } - - lessonList.lastUnlockedIndex = 0; - var lessonId = profileDataAccess.courseProgress(profile, course.id, ProfileDataAccess.LastUnlockedLesson); - if (lessonId !== "") { - for (var index = 0; index < course.lessonCount; index++) { - if (course.lesson(index).id === lessonId) { - lessonList.lastUnlockedIndex = index; - break; - } - } + if (course.lessonCount > 0) { + root.selectedLesson = course.lesson(0) + content.currentIndex = 0 } } onProfileChanged: update() onDataIndexCourseChanged: { - course.update() - update() + 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 (item.dataIndexCourse === null) + if (root.dataIndexCourse === null) { return - if (isValid && courseItem.id === dataIndexCourse.id) + } + 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 - dataAccess.loadCourse(dataIndexCourse, courseItem) + } + 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() } - LessonSelectorBase { + 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 + } - list:ScrollView { - anchors.fill: parent - ListView { + KeyboardLayoutMismatchMessage { + width: parent.width + 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 - id: lessonList - property int lastUnlockedIndex: 0 - model: course.isValid? course.lessonCount: 0 - spacing: 3 clip: true - delegate: ListItem { - property Lesson lesson: index < course.lessonCount? course.lesson(index): null - property bool locked: index > lessonList.lastUnlockedIndex - width: lessonList.width - onClicked: lessonList.currentIndex = index - onDoubleClicked: { - if (!locked) { - lessonSelected(course, lesson) + 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) } } - iconSource: locked? "object-locked": "" - label.opacity: locked? 0.5: 1.0 - title: lesson? lesson.title: "" + } + + 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 + } } - onModelChanged: update() } } - selectedLesson: lessonList.currentItem != null? lessonList.currentItem.lesson: null - selectedLessonLocked: lessonList.currentItem !== null && lessonList.currentItem.locked - onStartButtonClicked: lessonSelected(course, lessonList.currentItem.lesson) + 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 + } + } } } + diff --git a/src/qml/homescreen/LessonSelectorBase.qml b/src/qml/homescreen/LessonSelectorBase.qml deleted file mode 100644 --- a/src/qml/homescreen/LessonSelectorBase.qml +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Copyright 2013 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 1.3 -import QtQuick.Layouts 1.1 -import ktouch 1.0 - -Item { - id: root - - property alias list: listContainer.data - property alias previewArea: column - property Lesson selectedLesson - property bool selectedLessonLocked - signal startButtonClicked() - - - Row { - anchors.fill: parent - anchors.margins: 5 - spacing: 20 - - Item { - id: listContainer - height: parent.height - width: Math.round((parent.width - parent.spacing) / 2) - - } - - ColumnLayout { - id: column - width: parent.width - listContainer.width - parent.spacing - height: parent.height - spacing: 5 - - LessonPreview { - id: lessonPreview - Layout.fillWidth: true - Layout.fillHeight: true - - lesson: selectedLesson - } - - LessonLockedNotice { - anchors.centerIn: lessonPreview - blurSource: lessonPreview - opacity: selectedLessonLocked? 1: 0 - } - - Item { - id: startButtonContainer - Layout.fillWidth: true - height: Math.round(1.5 * startButton.height) - - Button { - id: startButton - anchors.centerIn: parent - text: i18n("Start Training") - enabled: selectedLesson !== null && !selectedLessonLocked - iconName: "go-next-view" - onClicked: startButtonClicked() - } - } - } - } -} diff --git a/src/qml/homescreen/LessonSelectorItem.qml b/src/qml/homescreen/LessonSelectorItem.qml new file mode 100644 --- /dev/null +++ b/src/qml/homescreen/LessonSelectorItem.qml @@ -0,0 +1,159 @@ +/* + * 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' + +Item { + id: root + property Lesson lesson + property bool selected + property bool editable: false + property alias background: background + + signal clicked + signal doubleClicked + signal deleteButtonClicked + signal editButtonClicked + signal statButtonClicked + + clip: true + + KColorScheme { + id: selectionColorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.Selection + } + + Rectangle { + id: background + anchors.fill: parent + color: selected? Qt.tint(selectionColorScheme.normalBackground, "#a0ffffff"): "#ffffff" + } + + MouseArea { + anchors.fill: parent + onClicked: { + root.clicked() + } + onDoubleClicked: { + root.doubleClicked() + } + } + + GridLayout { + id: content + anchors.fill: parent + anchors.margins: 10 + + Label { + id: titleLabel + Layout.column: 0 + Layout.row: 0 + Layout.fillWidth: true + Layout.preferredHeight: buttonRow.implicitHeight + text: lesson? lesson.title: "" + color: "#000000" + font.bold: true + elide: Label.ElideRight + verticalAlignment: Qt.AlignVCenter + + ToolTip { + parent: titleLabel + text: titleLabel.text + visible: titleMouseArea.containsMouse + } + + MouseArea { + id: titleMouseArea + anchors.fill: parent + acceptedButtons: Qt.NoButton + hoverEnabled: titleLabel.truncated + } + } + + Row { + id: buttonRow + Layout.column: 1 + Layout.row: 0 + visible: root.selected + + IconToolButton { + id: editButton + visible: root.editable + iconName: 'edit-entry' + color: "#000000" + backgroundColor: "#c0c0c0c0" + onClicked: { + root.editButtonClicked(); + } + } + + IconToolButton { + id: deleteButton + visible: root.editable + iconName: 'edit-delete' + color: "#000000" + backgroundColor: "#c0c0c0c0" + onClicked: { + root.deleteButtonClicked(); + } + } + + IconToolButton { + iconName: 'view-statistics' + color: "#000000" + backgroundColor: "#c0c0c0c0" + onClicked: { + root.statButtonClicked(); + } + } + } + + Item { + Layout.column: 0 + Layout.row: 1 + Layout.columnSpan: 2 + Layout.fillHeight: true + Layout.fillWidth: true + clip: true + + Label { + id: textLabel + anchors.fill: parent + text: lesson? lesson.text: "" + color: "#000000" + font.family: 'monospace' + lineHeight: 1.5 + scale: Math.min(1, width / implicitWidth) + transformOrigin: Item.TopLeft + } + + Rectangle { + anchors.fill: parent + gradient: Gradient { + GradientStop { position: 0.0; color: "#00000000" } + GradientStop { position: 0.8; color: Qt.rgba(background.color.r, background.color.g, background.color.b, 0) } + GradientStop { position: 1.0; color: background.color } + } + } + } + } +} + diff --git a/src/qml/homescreen/ProfileComboBox.qml b/src/qml/homescreen/ProfileComboBox.qml new file mode 100644 --- /dev/null +++ b/src/qml/homescreen/ProfileComboBox.qml @@ -0,0 +1,112 @@ +/* + * 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 QtGraphicalEffects 1.0 +import ktouch 1.0 + +import "../common" + +ComboBox { + id: root + + property Profile profile + property color manageProfileButtonBgColor + + model: profileDataAccess.profileCount + + contentItem: IconLabel { + iconName: "user-identity" + text: profile !== null? profile.name: "" + color: colorScheme.normalText + + } + + onActivated: { + profile = index < profileDataAccess.profileCount? profileDataAccess.profile(index): null + } + + onProfileChanged: { + for (var i = 0; i < profileDataAccess.profileCount; i++) { + if (profile === profileDataAccess.profile(i)) { + if (i != currentIndex) { + currentIndex = i; + } + break; + } + } + } + + delegate: ListItem { + width: root.width + text: index < profileDataAccess.profileCount? profileDataAccess.profile(index).name: "" + highlighted: root.currentIndex === index + } + + popupListView.footer: IconButton { + id: manageProfileButton + width: parent.width + color: root.colorScheme.normalText + bgColor: root.manageProfileButtonBgColor + text: i18n("Manage Profiles") + iconName: "user-properties" + MouseArea { + anchors.fill: parent + hoverEnabled: false + onPressed: { + mouse.accepted = true + } + onClicked: { + root.popup.close() + mouse.accepted = true + manageProfileDialog.open() + } + } + } + + PopupDialog { + id: manageProfileDialog + + margins: { + left: 40 + bottom: 40 + right: 40 + top: 40 + } + + width: parent.width - leftMargin - rightMargin + height: parent.height - topMargin - bottomMargin + modal: true + focus: true + title: i18n("Manage Profiles") + closePolicy: PopupDialog.CloseOnEscape + padding: 0 + contentItem: ProfileSelector { + id: profileSelector + onProfileChosen: { + root.profile = profile + manageProfileDialog.close() + } + } + onOpened: { + if (profileComboBox.profile) { + var index = profileDataAccess.indexOfProfile(profileComboBox.profile) + profileSelector.selectProfile(index) + } + } + } +} diff --git a/src/qml/homescreen/ProfileDetailsItem.qml b/src/qml/homescreen/ProfileDetailsItem.qml --- a/src/qml/homescreen/ProfileDetailsItem.qml +++ b/src/qml/homescreen/ProfileDetailsItem.qml @@ -1,6 +1,7 @@ /* * 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 @@ -16,11 +17,10 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 -import org.kde.kcoreaddons 1.0 +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" @@ -71,7 +71,7 @@ Rectangle { anchors.horizontalCenter: parent.horizontalCenter - width: parent.width - 40 + width: parent.width height: 250 color: activePalette.base @@ -150,12 +150,12 @@ topMargin: 5 } content: [ - ToolButton { + IconToolButton { iconName: "document-edit" text: i18n("Edit") onClicked: root.state = "editor" }, - ToolButton { + IconToolButton { iconName: "edit-delete" text: i18n("Delete") enabled: profileDataAccess.profileCount > 1 @@ -167,13 +167,13 @@ Item { id: editorContainer - width: parent.width - 40 + width: parent.width height: childrenRect.height anchors.centerIn: parent + ProfileForm { id: profileForm width: parent.width - height: childrenRect.height showWelcomeLabel: false onDone: { root.profile.name = profileForm.name @@ -192,7 +192,7 @@ Item { id: deleteConfirmationContainer - width: parent.width - 40 + width: parent.width height: childrenRect.height anchors.centerIn: parent Column { @@ -213,12 +213,13 @@ anchors.horizontalCenter: parent.horizontalCenter width: childrenRect.width height: childrenRect.height - ToolButton { + IconButton { iconName: "edit-delete" text: i18n("Delete") + bgColor: colorScheme.negativeBackground onClicked: root.deletionRequest() } - ToolButton { + IconButton { text: i18n("Cancel") onClicked: root.state = "info" } diff --git a/src/qml/homescreen/ProfileForm.qml b/src/qml/homescreen/ProfileForm.qml --- a/src/qml/homescreen/ProfileForm.qml +++ b/src/qml/homescreen/ProfileForm.qml @@ -16,22 +16,21 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 import ktouch 1.0 import "../common" -Column { +ColumnLayout { id: root property alias name: nameTextField.text property int skillLevel: 0 property bool skillLevelSelectionEnabled: true property alias showWelcomeLabel: welcomeLabel.visible - property alias doneButtonIconSource: doneBtn.iconSource + property alias doneButtonIconSource: doneBtn.iconName property alias doneButtonText: doneBtn.text signal done() @@ -44,50 +43,73 @@ Label { id: welcomeLabel - width: parent.width + Layout.fillWidth: true + Layout.preferredWidth: 500 text: i18n("Before you start training, please introduce yourself:") } TextField { id: nameTextField - width: parent.width + Layout.fillWidth: true placeholderText: i18n("Name") } - DetailedRadioButton { + RadioButton { id: beginnerRadioButton - width: parent.width + Layout.maximumWidth: parent.width enabled: root.skillLevelSelectionEnabled - label: i18n("I have no or only very little experience in machine typing") - hint: i18n("Lessons are unlocked as your typing skills improve over time.") + text: i18n("I have no or only very little experience in machine typing") + label.wrapMode: Text.Wrap onCheckedChanged: { if (checked) { root.skillLevel = Profile.Beginner - advancedRadioButton.checked = false } } } - DetailedRadioButton { + Label { + text: i18n("Lessons are unlocked as your typing skills improve over time.") + wrapMode: Text.Wrap + Layout.maximumWidth: parent.width + leftPadding: font.pixelSize * 2 + font.italic: true + enabled: root.skillLevelSelectionEnabled + } + + RadioButton { id: advancedRadioButton - width: parent.width + Layout.maximumWidth: parent.width enabled: root.skillLevelSelectionEnabled - label: i18n("I am an experienced machine typist and want to improve my skills") - hint: i18n("All lessons are unlocked immediately.") + text: i18n("I am an experienced machine typist and want to improve my skills") + label.wrapMode: Text.Wrap onCheckedChanged: { if (checked) { root.skillLevel = Profile.Advanced - beginnerRadioButton.checked = false } } } - Button { - id: doneBtn - anchors.horizontalCenter: parent.horizontalCenter - text: i18n("Done") - enabled: nameTextField.text !== "" && (beginnerRadioButton.checked || advancedRadioButton.checked) - iconName: "dialog-ok" - onClicked: done() + Label { + text: i18n("All lessons are unlocked immediately.") + wrapMode: Text.Wrap + Layout.maximumWidth: parent.width + leftPadding: font.pixelSize * 2 + font.italic: true + enabled: root.skillLevelSelectionEnabled + } + + Item { + Layout.fillWidth: true + Layout.preferredHeight: doneBtn.implicitHeight + + IconButton { + id: doneBtn + anchors.horizontalCenter: parent.horizontalCenter + bgColor: colorScheme.positiveBackground + text: i18n("Done") + enabled: nameTextField.text !== "" && (beginnerRadioButton.checked || advancedRadioButton.checked) + iconName: "dialog-ok" + onClicked: done() + } } } diff --git a/src/qml/homescreen/ProfileSelector.qml b/src/qml/homescreen/ProfileSelector.qml --- a/src/qml/homescreen/ProfileSelector.qml +++ b/src/qml/homescreen/ProfileSelector.qml @@ -1,6 +1,7 @@ /* * 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 @@ -16,9 +17,8 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 import ktouch 1.0 import "../common" @@ -38,52 +38,61 @@ profileForm.profile = profileDataAccess.profile(index) } + KColorScheme { + id: listColorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.View + } + ColumnLayout { anchors.fill: parent - anchors.bottomMargin: 10 - spacing: 10 + anchors.margins: 20 + spacing: 20 RowLayout { Layout.fillWidth: true Layout.fillHeight: true - spacing: 10 + spacing: 20 - Item { - id: listContainer + ListView { + id: list Layout.fillWidth: true Layout.fillHeight: true - - ScrollView { - anchors.fill: parent - ListView { - id: list - anchors.fill: parent - anchors.margins: 3 - spacing: 3 - model: profileDataAccess.profileCount + 1 - clip: true - delegate: ListItem { - property bool isNewButton: index >= profileDataAccess.profileCount - width: list.width - title: isNewButton? - i18n("Create New Profile"): - index < profileDataAccess.profileCount? profileDataAccess.profile(index).name: null - label.font.italic: isNewButton - iconSource: isNewButton? "list-add": "user-identity" - onClicked: { - list.currentIndex = index - if (isNewButton) { - createNewProfile() - } - else { - selectProfile(index) - } - } - onDoubleClicked: { - if (!isNewButton) { - root.profileChosen(profileDataAccess.profile(list.currentIndex)) - } - } + model: profileDataAccess.profileCount + 1 + clip: true + focus: true + delegate: ListItem { + property bool isNewButton: index >= profileDataAccess.profileCount + width: list.width + text: isNewButton? + i18n("Create New Profile"): + index < profileDataAccess.profileCount? profileDataAccess.profile(index).name: null + label.font.italic: isNewButton + iconName: isNewButton? "list-add": "user-identity" + highlighted: ListView.isCurrentItem + onClicked: { + list.forceActiveFocus() + list.currentIndex = index + if (isNewButton) { + createNewProfile() + } + else { + selectProfile(index) + } + } + onDoubleClicked: { + if (!isNewButton) { + root.profileChosen(profileDataAccess.profile(list.currentIndex)) + } + } + } + onCurrentItemChanged: { + if (currentItem != null) { + if (currentItem.isNewButton) { + createNewProfile() + } + else { + selectProfile(currentIndex) } } } @@ -103,12 +112,13 @@ } } - Button { + IconButton { id: selectButton - anchors.horizontalCenter: parent.horizontalCenter + Layout.fillWidth: true iconName: "go-next-view" text: i18n("Use Selected Profile") enabled: list.currentIndex !== -1 && list.currentIndex < profileDataAccess.profileCount + bgColor: colorScheme.positiveBackground onClicked: root.profileChosen(profileDataAccess.profile(list.currentIndex)) } } diff --git a/src/qml/homescreen/StatPopupDialog.qml b/src/qml/homescreen/StatPopupDialog.qml new file mode 100644 --- /dev/null +++ b/src/qml/homescreen/StatPopupDialog.qml @@ -0,0 +1,90 @@ +/* + * 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 + model: LearningProgressModel { + id: learningProgressModel + } + } + + Row { + spacing: 2 * titleLabel.font.pixelSize + Layout.alignment: Qt.AlignTop | Qt.AlignHCenter + Charts.LegendItem { + id: accuracyLegend + dimension: learningProgressChart.accuracy + } + Charts.LegendItem { + id: charactersPerMinuteLegend + dimension: learningProgressChart.charactersPerMinute + } + } + } + KColorScheme { + id: colorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.View + } + } +} diff --git a/src/qml/keyboard/KeyItem.qml b/src/qml/keyboard/KeyItem.qml --- a/src/qml/keyboard/KeyItem.qml +++ b/src/qml/keyboard/KeyItem.qml @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 import QtGraphicalEffects 1.0 import ktouch 1.0 @@ -119,7 +119,7 @@ } PropertyChanges { target: shadowEffect - glowRadius: 10 + glowRadius: body.radius } }, State { @@ -131,7 +131,7 @@ } PropertyChanges { target: shadowEffect - glowRadius: 15 + glowRadius: 1.5 * body.radius } }, State { @@ -143,7 +143,7 @@ } PropertyChanges { target: shadowEffect - glowRadius: 15 + glowRadius: 1.5 * body.radius } } ] diff --git a/src/qml/keyboard/KeyLabel.qml b/src/qml/keyboard/KeyLabel.qml --- a/src/qml/keyboard/KeyLabel.qml +++ b/src/qml/keyboard/KeyLabel.qml @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 import ktouch 1.0 Text { diff --git a/src/qml/keyboard/Keyboard.qml b/src/qml/keyboard/Keyboard.qml --- a/src/qml/keyboard/Keyboard.qml +++ b/src/qml/keyboard/Keyboard.qml @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 import ktouch 1.0 Item { diff --git a/src/qml/keyboard/KeyboardLayoutEditor.qml b/src/qml/keyboard/KeyboardLayoutEditor.qml --- a/src/qml/keyboard/KeyboardLayoutEditor.qml +++ b/src/qml/keyboard/KeyboardLayoutEditor.qml @@ -15,7 +15,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 import ktouch 1.0 import "../common" @@ -44,7 +44,7 @@ } } - Grid { + LineGrid { id: keyContainer anchors.centerIn: parent diff --git a/src/qml/main.qml b/src/qml/main.qml --- a/src/qml/main.qml +++ b/src/qml/main.qml @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 import ktouch 1.0 import "./common" @@ -26,20 +26,22 @@ import "./scorescreen" Rectangle { - SystemPalette { - id: activePallete - colorGroup: SystemPalette.Active - } - id: main - color: activePallete.window + color: activePallete.normalBackground + property Item appContent: appContentItem function switchScreen(from, to) { switchScreenAnimation.from = from switchScreenAnimation.to = to switchScreenAnimation.start() } + KColorScheme { + id: activePallete + colorGroup: KColorScheme.Active + colorSet: KColorScheme.Window + } + DataAccess { id: dataAccess } @@ -50,11 +52,11 @@ property int keyboardLayoutCount: ktouch.globalDataIndex.keyboardLayoutCount property int courseCount: ktouch.globalDataIndex.courseCount onNameChanged: { - keyboardLayout.update() + activeKeyboardLayout.update() } onKeyboardLayoutCountChanged: { if (ktouch.globalDataIndex.isValid) - keyboardLayout.update() + activeKeyboardLayout.update() } } @@ -72,11 +74,11 @@ } KeyboardLayout { - id: keyboardLayout + id: activeKeyboardLayout Component.onCompleted: { if (ktouch.globalDataIndex.isValid) { - keyboardLayout.update() + activeKeyboardLayout.update() } } @@ -91,7 +93,7 @@ var dataIndexLayout = ktouch.globalDataIndex.keyboardLayout(i) if (dataIndexLayout.name === name) { - dataAccess.loadKeyboardLayout(dataIndexLayout, keyboardLayout) + dataAccess.loadKeyboardLayout(dataIndexLayout, activeKeyboardLayout) return } } @@ -103,20 +105,13 @@ var dataIndexLayout = ktouch.globalDataIndex.keyboardLayout(i) if (name.search(dataIndexLayout.name) === 0) { - dataAccess.loadKeyboardLayout(dataIndexLayout, keyboardLayout) + dataAccess.loadKeyboardLayout(dataIndexLayout, activeKeyboardLayout) return } } } } - CategorizedResourceSortFilterProxyModel { - id: availableCourseModel - resourceModel: resourceModel - resourceTypeFilter: ResourceModel.CourseItem - keyboardLayoutNameFilter: keyboardLayout.isValid? keyboardLayout.name: ktouch.keyboardLayoutName - } - Course { id: selectedCourse property Lesson selectedLesson @@ -126,67 +121,72 @@ id: customLessonCopy } - HomeScreen { - id: homeScreen + Item { anchors.fill: parent - courseModel: availableCourseModel - keyboardLayout: keyboardLayout - keyboardLayoutName: keyboardLayout.isValid? keyboardLayout.name: helper.name - visible: false - focus: true - onLessonSelected: { - trainingScreen.profile = profile - var lessonIndex = -1; - for (var i = 0; i < course.lessonCount; i++) { - if (lesson === course.lesson(i)) { - lessonIndex = i - break + id: appContentItem + layer.enabled: true + + HomeScreen { + id: homeScreen + anchors.fill: parent + activeKeyboardLayoutName: activeKeyboardLayout.isValid? activeKeyboardLayout.name: helper.name + visible: false + focus: true + onLessonSelected: { + trainingScreen.profile = profile + var lessonIndex = -1; + for (var i = 0; i < course.lessonCount; i++) { + if (lesson === course.lesson(i)) { + lessonIndex = i + break + } } - } - selectedCourse.copyFrom(course) + selectedCourse.copyFrom(course) - if (lessonIndex !== -1) { - selectedCourse.selectedLesson = selectedCourse.lesson(lessonIndex) + if (lessonIndex !== -1) { + selectedCourse.selectedLesson = selectedCourse.lesson(lessonIndex) + } + else { + customLessonCopy.copyFrom(lesson) + selectedCourse.selectedLesson = customLessonCopy + } + + main.switchScreen(homeScreen, trainingScreen) } - else { - customLessonCopy.copyFrom(lesson) - selectedCourse.selectedLesson = customLessonCopy + Component.onCompleted: { + homeScreen.reset() + homeScreen.visible = true + homeScreen.start() } - - main.switchScreen(homeScreen, trainingScreen) } - Component.onCompleted: { - homeScreen.reset() - homeScreen.visible = true - } - } - TrainingScreen { - id: trainingScreen - anchors.fill: parent - visible: false - keyboardLayout: keyboardLayout - course: selectedCourse - lesson: selectedCourse.selectedLesson - onRestartRequested: main.switchScreen(trainingScreen, trainingScreen) - onAbortRequested: main.switchScreen(trainingScreen, homeScreen) - onFinished: main.switchScreen(trainingScreen, scoreScreen) - } + TrainingScreen { + id: trainingScreen + anchors.fill: parent + visible: false + keyboardLayout: homeScreen.selectedKeyboardLayout + course: selectedCourse + lesson: selectedCourse.selectedLesson + onRestartRequested: main.switchScreen(trainingScreen, trainingScreen) + onAbortRequested: main.switchScreen(trainingScreen, homeScreen) + onFinished: main.switchScreen(trainingScreen, scoreScreen) + } - ScoreScreen { - id: scoreScreen - anchors.fill: parent - visible: false - course: trainingScreen.course - lesson: trainingScreen.lesson - stats: trainingScreen.stats - profile: trainingScreen.profile - referenceStats: trainingScreen.referenceStats - onHomeScreenRequested: main.switchScreen(scoreScreen, homeScreen) - onLessonRepetionRequested: main.switchScreen(scoreScreen, trainingScreen) - onNextLessonRequested: { - selectedCourse.selectedLesson = lesson - main.switchScreen(scoreScreen, trainingScreen) + ScoreScreen { + id: scoreScreen + anchors.fill: parent + visible: false + course: trainingScreen.course + lesson: trainingScreen.lesson + stats: trainingScreen.stats + profile: trainingScreen.profile + referenceStats: trainingScreen.referenceStats + onHomeScreenRequested: main.switchScreen(scoreScreen, homeScreen) + onLessonRepetionRequested: main.switchScreen(scoreScreen, trainingScreen) + onNextLessonRequested: { + selectedCourse.selectedLesson = lesson + main.switchScreen(scoreScreen, trainingScreen) + } } } diff --git a/src/qml/meters/AccuracyMeter.qml b/src/qml/meters/AccuracyMeter.qml --- a/src/qml/meters/AccuracyMeter.qml +++ b/src/qml/meters/AccuracyMeter.qml @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 import ktouch 1.0 Meter { @@ -32,7 +32,7 @@ analogPartContent: Image { anchors.centerIn: parent - source: utils.findImage("accuracymeter-background.png") + source: "qrc:///ktouch/images/accuracymeter-background.png" ScaleBackgroundItem { anchors.centerIn: parent @@ -49,7 +49,7 @@ id: scale anchors.centerIn: parent anchors.verticalCenterOffset: 25 - source: utils.findImage("accuracymeter-scale.png") + source: "qrc:///ktouch/images/accuracymeter-scale.png" } Text { @@ -76,7 +76,7 @@ id: hand anchors.centerIn: parent anchors.verticalCenterOffset: 25 - source: utils.findImage("accuracymeter-hand.png") + source: "qrc:///ktouch/images/accuracymeter-hand.png" transform: Rotation { origin.x: hand.width / 2 origin.y: hand.height / 2 diff --git a/src/qml/meters/CharactersPerMinuteMeter.qml b/src/qml/meters/CharactersPerMinuteMeter.qml --- a/src/qml/meters/CharactersPerMinuteMeter.qml +++ b/src/qml/meters/CharactersPerMinuteMeter.qml @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 import ktouch 1.0 Meter { @@ -35,7 +35,7 @@ analogPartContent: Image { anchors.centerIn: parent - source: utils.findImage("charactersperminutemeter-background.png") + source: "qrc:///ktouch/images/charactersperminutemeter-background.png" ScaleBackgroundItem { anchors.centerIn: parent @@ -52,7 +52,7 @@ id: scale anchors.centerIn: parent anchors.verticalCenterOffset: 25 - source: utils.findImage("charactersperminutemeter-scale.png") + source: "qrc:///ktouch/images/charactersperminutemeter-scale.png" } Text { @@ -79,7 +79,7 @@ id: hand anchors.centerIn: parent anchors.verticalCenterOffset: 25 - source: utils.findImage("charactersperminutemeter-hand.png") + source: "qrc:///ktouch/images/charactersperminutemeter-hand.png" smooth: true transform: Rotation { origin.x: hand.width / 2 diff --git a/src/qml/meters/ElapsedTimeMeter.qml b/src/qml/meters/ElapsedTimeMeter.qml --- a/src/qml/meters/ElapsedTimeMeter.qml +++ b/src/qml/meters/ElapsedTimeMeter.qml @@ -16,7 +16,7 @@ * along with this program. If not, see . */ -import QtQuick 2.4 +import QtQuick 2.9 import ktouch 1.0 Meter { @@ -32,12 +32,12 @@ analogPartContent: Image { anchors.centerIn: parent - source: utils.findImage("elapsedtimemeter-background.png") + source: "qrc:///ktouch/images/elapsedtimemeter-background.png" Image { id: minuteHand anchors.centerIn: parent - source: utils.findImage("elapsedtimemeter-minute-hand.png") + source: "qrc:///ktouch/images/elapsedtimemeter-minute-hand.png" smooth: true transform: Rotation { origin.x: minuteHand.width / 2 @@ -52,7 +52,7 @@ Image { id: secondHand anchors.centerIn: parent - source: utils.findImage("elapsedtimemeter-second-hand.png") + source: "qrc:///ktouch/images/elapsedtimemeter-second-hand.png" transform: Rotation { origin.x: secondHand.width / 2 origin.y: secondHand.height / 2 diff --git a/src/qml/meters/Meter.qml b/src/qml/meters/Meter.qml --- a/src/qml/meters/Meter.qml +++ b/src/qml/meters/Meter.qml @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 RowLayout { id: meter @@ -43,7 +43,7 @@ right:6 bottom: 6 } - source: utils.findImage("meterbox-left.png") + source: "qrc:///ktouch/images/meterbox-left.png" } BorderImage { @@ -56,7 +56,7 @@ right:6 bottom: 6 } - source: utils.findImage("meterbox-right.png") + source: "qrc:///ktouch/images/meterbox-right.png" Column { anchors { diff --git a/src/qml/meters/StatBox.qml b/src/qml/meters/StatBox.qml --- a/src/qml/meters/StatBox.qml +++ b/src/qml/meters/StatBox.qml @@ -16,8 +16,8 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 import ktouch 1.0 RowLayout { diff --git a/src/qml/qml.qrc b/src/qml/qml.qrc --- a/src/qml/qml.qrc +++ b/src/qml/qml.qrc @@ -1,44 +1,67 @@ - - main.qml - meters/AccuracyMeter.qml - meters/CharactersPerMinuteMeter.qml - meters/ElapsedTimeMeter.qml - meters/Meter.qml - meters/StatBox.qml - common/DetailedRadioButton.qml + + common/AutoTriggerButton.qml + common/Balloon.qml + common/Collapsable.qml + common/ComboBox.qml + common/FocusBar.qml + common/GridView.qml + common/Icon.qml + common/IconButton.qml + common/IconLabel.qml + common/IconToolButton.qml common/InfoItem.qml common/InformationTable.qml common/InlineToolbar.qml + common/Label.qml common/LearningProgressChart.qml common/ListItem.qml + common/ListView.qml common/MessageBox.qml + common/MonochromeIcon.qml + common/PopupDialog.qml + common/RadioButton.qml + common/ScrollBar.qml + common/ScrollView.qml common/SelectionGrip.qml common/SelectionRectangle.qml - common/SheetDialog.qml - common/Balloon.qml - scorescreen/ScoreScreen.qml - trainingscreen/KeyboardUnavailableNotice.qml - trainingscreen/TrainingScreenMenuOverlay.qml - trainingscreen/TrainingScreen.qml - trainingscreen/TrainingScreenToolbar.qml - trainingscreen/TrainingWidget.qml + common/TextArea.qml + common/TextField.qml + common/ToolBar.qml + common/ToolSeparator.qml + common/ToolTip.qml homescreen/CourseDescriptionItem.qml - homescreen/CoursePage.qml homescreen/CourseSelector.qml - homescreen/InitialProfileForm.qml - homescreen/LessonLockedNotice.qml - homescreen/LessonPreview.qml - homescreen/LessonSelectorBase.qml + homescreen/CourseSelectorKeyboardLayoutItem.qml + homescreen/CourseSelectorKeyboardLayoutList.qml homescreen/HomeScreen.qml - homescreen/CustomLessonSelector.qml - homescreen/ProfileForm.qml + homescreen/InitialProfileDialog.qml + homescreen/KeyboardLayoutMismatchMessage.qml + homescreen/LessonDeletedMessage.qml + homescreen/LessonEditorDialog.qml + homescreen/LessonLockedNotice.qml homescreen/LessonSelector.qml - homescreen/ProfileSelector.qml + homescreen/LessonSelectorItem.qml + homescreen/ProfileComboBox.qml homescreen/ProfileDetailsItem.qml - keyboard/Keyboard.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/TrainingScreenMenu.qml + trainingscreen/TrainingScreenToolbar.qml + trainingscreen/TrainingWidget.qml diff --git a/src/qml/scorescreen/ScoreScreen.qml b/src/qml/scorescreen/ScoreScreen.qml --- a/src/qml/scorescreen/ScoreScreen.qml +++ b/src/qml/scorescreen/ScoreScreen.qml @@ -1,6 +1,5 @@ /* - * Copyright 2012 Sebastian Gottfried - * Copyright 2016 Sebastian Gottfried + * 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 @@ -16,11 +15,9 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 import ktouch 1.0 -import org.kde.kquickcontrolsaddons 2.0 import org.kde.charts 0.1 as Charts import "../common" @@ -104,13 +101,16 @@ } - SystemPalette { - id: palette - colorGroup: SystemPalette.Active + KColorScheme { + id: colorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.View } + + LearningProgressModel { - property bool filterByLesson: false + property bool filterByLesson: learningProgressFilterComboBox.currentIndex == 1 id: learningProgressModel profile: screen.visible? screen.profile: null courseFilter: screen.visible? screen.course: null @@ -122,101 +122,6 @@ trainingStats: screen.visible? screen.stats: null } - Balloon { - id: chartTypeDialog - visualParent: chartTypeButton - - Column { - id: chartTypeDialogContents - - Button { - id: progressChartButton - width: Math.max(implicitWidth, errorsChartButton.implicitWidth) - text: i18n("Progress") - iconName: "office-chart-area" - onClicked: { - chartTypeDialog.close() - tabGroup.currentIndex = 0 - } - } - Button { - id: errorsChartButton - width: Math.max(implicitWidth, progressChartButton.implicitWidth) - text: i18n("Errors") - iconName: "office-chart-bar" - onClicked: { - chartTypeDialog.close() - tabGroup.currentIndex = 1 - } - } - } - } - - Balloon { - id: learningProgressDialog - visualParent: learningProgressFilterButton - - Column { - id: learningProgressDialogContents - - Button { - id: allLessonsButton - width: Math.max(implicitWidth, thisLessonButton.implicitWidth) - text: i18n("All Lessons") - iconName: "view-filter" - onClicked: { - learningProgressModel.filterByLesson = false - learningProgressDialog.close() - } - } - Button { - id: thisLessonButton - width: Math.max(implicitWidth, allLessonsButton.implicitWidth) - text: i18n("This Lesson") - iconName: "view-filter" - onClicked: { - learningProgressModel.filterByLesson = true - learningProgressDialog.close() - } - } - } - } - - Balloon { - id: learningProgressPointTooltip - visualParent: parent - property int row: -1 - - function findLessonTitle(id) { - var course = screen.course - for (var i = 0; i < course.lessonCount; i++) { - if (course.lesson(i).id === id) { - return course.lesson(i).title - } - } - return i18n("Unknown") - } - - InformationTable { - property list infoModel: [ - InfoItem { - title: i18nc("Statistics on lesson:", "On:") - text: learningProgressPointTooltip.row !== -1? learningProgressPointTooltip.findLessonTitle(learningProgressModel.lessonId(learningProgressPointTooltip.row)): "" - }, - InfoItem { - title: i18n("Accuracy:") - text: learningProgressPointTooltip.row !== -1? strFormatter.formatAccuracy(learningProgressModel.accuracy(learningProgressPointTooltip.row)): "" - }, - InfoItem { - title: i18n("Characters per Minute:") - text: learningProgressPointTooltip.row !== -1? learningProgressModel.charactersPerMinute(learningProgressPointTooltip.row): "" - } - ] - width: 250 - model: infoModel - } - } - Balloon { id: errorsTooltip visualParent: parent @@ -250,44 +155,46 @@ RowLayout { anchors.fill: parent - anchors.leftMargin: 10 - anchors.rightMargin: 10 + anchors.leftMargin: 20 + anchors.rightMargin: 20 spacing: anchors.leftMargin - Button { + IconToolButton { id: homeScreenButton - anchors.verticalCenter: parent.verticalCenter text: i18n("Return to Home Screen") iconName: "go-home" + colorScheme.colorSet: KColorScheme.Complementary onClicked: screen.homeScreenRequested() } Item { Layout.fillHeight: true Layout.fillWidth: true } - Button { + IconToolButton { id: repeatLessonButton iconName: "view-refresh" text: i18n("Repeat Lesson") + colorScheme.colorSet: KColorScheme.Complementary onClicked: screen.lessonRepetionRequested() } - Button { + 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: palette.base + color: colorScheme.normalBackground ColumnLayout { id: content @@ -313,7 +220,7 @@ anchors.horizontalCenter: parent.horizontalCenter width: captionIcon.width + captionLabel.width + 7 height: Math.max(captionIcon.height, captionLabel.height) - QIconItem { + Icon { id: captionIcon anchors.left: parent.left anchors.verticalCenter: parent.verticalCenter @@ -326,8 +233,8 @@ 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.") + 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" } @@ -377,154 +284,133 @@ Label { id: showLabel - color: "#888" - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignVCenter text: i18nc("Show a specific type of statistic", "Show") + opacity: 0.7 } - Button { - id: chartTypeButton - anchors.verticalCenter: parent.verticalCenter - text: tabGroup.currentTab.title - iconName: tabGroup.currentTab.iconName - Layout.preferredWidth: chartTypeDialogContents.width - checked: chartTypeDialog.status === 'open' || chartTypeDialog.status === 'opening' - onClicked: { - if (checked) { - chartTypeDialog.close() - } - else { - chartTypeDialog.open() + 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 - color: "#888" - anchors.verticalCenter: parent.verticalCenter + Layout.alignment: Qt.AlignVCenter text: i18nc("Show a statistic over one or more lessons", "Over") - opacity: tabGroup.currentTab === learningProgressTab? 1: 0 + opacity: tabGroup.currentItem === learningProgressTab? 0.7: 0 Behavior on opacity { NumberAnimation {duration: 150} } } - Button { - id: learningProgressFilterButton - anchors.verticalCenter: parent.verticalCenter - text: learningProgressModel.filterByLesson? thisLessonButton.text: allLessonsButton.text - iconName: "view-filter" - checked: chartTypeDialog.status === 'open' || chartTypeDialog.status === 'opening' - opacity: tabGroup.currentTab === learningProgressTab? 1: 0 - onClicked: { - if (checked) { - learningProgressDialog.close() - } - else { - learningProgressDialog.open() + ComboBox { + id: learningProgressFilterComboBox + Layout.alignment: Qt.AlignVCenter + model: ListModel { + Component.onCompleted: { + append({"text": i18n("All Lessons"), "icon": "view-filter"}); + append({"text": i18n("This Lessons"), "icon": "view-filter"}); } } + textRole: "text" + currentIndex: 0 + opacity: tabGroup.currentItem === learningProgressTab? 1: 0 Behavior on opacity { NumberAnimation {duration: 150} } } Item { - Layout.fillHeight: true Layout.fillWidth: true } Charts.LegendItem { id: accuracyLegend - textColor: palette.text - anchors.verticalCenter: parent.verticalCenter - opacity: tabGroup.currentTab === learningProgressTab? 1: 0 + textColor: colorScheme.normalText + Layout.alignment: Qt.AlignVCenter + opacity: tabGroup.currentItem === learningProgressTab? 1: 0 Behavior on opacity { NumberAnimation {duration: 150} } } Charts.LegendItem { id: charactersPerMinuteLegend - textColor: palette.text - anchors.verticalCenter: parent.verticalCenter - opacity: tabGroup.currentTab === learningProgressTab? 1: 0 + textColor: colorScheme.normalText + Layout.alignment: Qt.AlignVCenter + opacity: tabGroup.currentItem === learningProgressTab? 1: 0 Behavior on opacity { NumberAnimation {duration: 150} } } } - TabView { - id: tabGroup + Item { Layout.fillHeight: true Layout.fillWidth: true - frameVisible: false - tabsVisible: false - property Tab currentTab: count > 0 && currentIndex != -1? getTab(currentIndex): null - Tab { - id: learningProgressTab - title: i18n("Progress") - property string iconName: "office-chart-area" - LearningProgressChart { - id: learningProgressChart - anchors.fill: parent - model: learningProgressModel - backgroundColor: palette.base + StackLayout { + anchors.fill: parent + id: tabGroup + currentIndex: chartTypeComboBox.currentIndex + property Item currentItem: currentIndex != -1? children[currentIndex]: null - onElemEntered: { - learningProgressPointTooltip.visualParent = elem - learningProgressPointTooltip.row = row - learningProgressPointTooltip.open() - } - onElemExited: { - learningProgressPointTooltip.close() - } + LearningProgressChart { + id: learningProgressTab + property string title: i18n("Progress") + property string iconName: "office-chart-area" + model: learningProgressModel + backgroundColor: colorScheme.normalBackground Component.onCompleted: { - accuracyLegend.dimension = learningProgressChart.accuracy - charactersPerMinuteLegend.dimension = learningProgressChart.charactersPerMinute + accuracyLegend.dimension = learningProgressTab.accuracy + charactersPerMinuteLegend.dimension = learningProgressTab.charactersPerMinute } Component.onDestruction: { - accuracyLegend.dimension = 0 - charactersPerMinuteLegend.dimension = 0 + accuracyLegend.dimension = null + charactersPerMinuteLegend.dimension = null } } - } - Tab { - id: errorsTab - title: i18n("Errors") - property string iconName: "office-chart-bar" - - Charts.BarChart{ - anchors.fill: parent - model: errorsModel - pitch: 60 - textRole: 3 // Qt::ToolTipRole - backgroundColor: palette.base - - dimensions: [ - Charts.Dimension { - dataColumn: 0 - color: "#ffb12d" - maximumValue: Math.max(4, Math.ceil(errorsModel.maximumErrorCount / 4) * 4) - label: i18n("Errors") + Item { + id: errorsTab + property string title: i18n("Errors") + property string iconName: "office-chart-bar" + + Charts.BarChart{ + 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() } - ] - onElemEntered: { - errorsTooltip.visualParent = elem; - errorsTooltip.row = row - errorsTooltip.open() - } - - onElemExited: { - errorsTooltip.close() + onElemExited: { + errorsTooltip.close() + } } } } diff --git a/src/qml/trainingscreen/KeyboardUnavailableNotice.qml b/src/qml/trainingscreen/KeyboardUnavailableNotice.qml --- a/src/qml/trainingscreen/KeyboardUnavailableNotice.qml +++ b/src/qml/trainingscreen/KeyboardUnavailableNotice.qml @@ -1,6 +1,5 @@ /* - * Copyright 2012 Sebastian Gottfried - * Copyright 2015 Sebastian Gottfried + * 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 @@ -16,38 +15,24 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 -import org.kde.kquickcontrolsaddons 2.0 +import QtQuick 2.9 import ktouch 1.0 +import '../common' + Item { id: root - height: row.height + 20 + property KColorScheme colorScheme: KColorScheme {} + + height: label.height + 20 - Row { - id: row + IconLabel { + id: label anchors.centerIn: parent width: parent.width - 60 - height: Math.max(icon.height, label.height) - spacing: 3 - - QIconItem { - id: icon - anchors.verticalCenter: parent.verticalCenter - width: 22 - height: 22 - icon: "dialog-warning" - } - - Label { - id: label - anchors.verticalCenter: parent.verticalCenter - width: parent.width - icon.width - parent.spacing - text: i18n("No visualization available for your keyboard layout.") - color: "white" - } + iconName: "dialog-warning" + text: i18n("No visualization available for your keyboard layout.") + color: colorScheme.normalText } } diff --git a/src/qml/trainingscreen/TrainingScreen.qml b/src/qml/trainingscreen/TrainingScreen.qml --- a/src/qml/trainingscreen/TrainingScreen.qml +++ b/src/qml/trainingscreen/TrainingScreen.qml @@ -1,6 +1,5 @@ /* - * Copyright 2013 Sebastian Gottfried - * Copyright 2015 Sebastian Gottfried + * 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 @@ -16,11 +15,11 @@ * along with this program. If not, see . */ -import QtQuick 2.5 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 import ktouch 1.0 +import "../common" import "../keyboard" import "../meters" @@ -112,6 +111,12 @@ } } + KColorScheme { + id: colorScheme + colorGroup: KColorScheme.Active + colorSet: KColorScheme.Complementary + } + TrainingStats { id: stats onTimeIsRunningChanged: { @@ -129,14 +134,10 @@ sequence: "Escape" enabled: screen.visible onActivated: { - if (menuOverlay.active) { - menuOverlay.hide() - } - else { - menuOverlay.show() + if (!menu.visible) { + menu.open() } } - } @@ -152,7 +153,7 @@ bottom: 1 } cache: false - source: utils.findImage("trainingscreen-toolbar.png") + source: "qrc:///ktouch/images/trainingscreen-toolbar.png" horizontalTileMode: BorderImage.Repeat verticalTileMode: BorderImage.Repeat @@ -162,7 +163,7 @@ trainingStarted: screen.trainingStarted trainingFinished: screen.trainingFinished stats: stats - menuOverlayItem: menuOverlay + menu: menu } } @@ -175,7 +176,7 @@ top: 1 bottom: 1 } - source: utils.findImage("trainingscreen-header.png") + source: "qrc:///ktouch/images/trainingscreen-header.png" cache: false StatBox { @@ -195,7 +196,7 @@ top: 1 bottom: 1 } - source: utils.findImage("trainingscreen-viewport.png") + source: "qrc:///ktouch/images/trainingscreen-viewport.png" cache: false TrainingWidget { @@ -222,7 +223,7 @@ top: 3 bottom: 3 } - source: utils.findImage("trainingscreen-viewport-shadow.png") + source: "qrc:///ktouch/images/trainingscreen-viewport-shadow.png" cache: false } @@ -242,8 +243,9 @@ top: 1 bottom: 1 } - source: utils.findImage("trainingscreen-footer.png") + source: "qrc:///ktouch/images/trainingscreen-footer.png" cache: false + Keyboard { id: keyboard @@ -310,7 +312,9 @@ KeyboardUnavailableNotice { id: keyboardUnavailableNotice + colorScheme: colorScheme visible: !screen.keyboardLayout.isValid + width: parent.width } } } @@ -320,10 +324,8 @@ anchors.fill: parent } - TrainingScreenMenuOverlay { - id: menuOverlay - blurSource: screenContent - anchors.fill: parent + TrainingScreenMenu { + id: menu onClosed: trainingWidget.forceActiveFocus() onRestartRequested: screen.restartRequested() onAbortRequested: screen.abortRequested() diff --git a/src/qml/trainingscreen/TrainingScreenMenu.qml b/src/qml/trainingscreen/TrainingScreenMenu.qml new file mode 100644 --- /dev/null +++ b/src/qml/trainingscreen/TrainingScreenMenu.qml @@ -0,0 +1,93 @@ +/* + * 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 '../common' + + +PopupDialog { + id: root + + signal restartRequested() + signal abortRequested() + + onOpened: { + resumeButton.forceActiveFocus() + } + + title: i18n("Menu") + + padding: 20 + + contentItem: Item { + width: column.width + height: column.height + + Column { + id: column + focus: true + anchors.centerIn: parent + spacing: 20 + width: 1.5 * Math.max(resumeButton.implicitWidth, restartButton.implicitWidth, returnButton.implicitWidth) + + IconButton { + id: resumeButton + iconName: "go-next-view" + text: i18n("Resume Training") + bgColor: colorScheme.positiveBackground + width: parent.width + onClicked: close() + KeyNavigation.backtab: returnButton + KeyNavigation.tab: restartButton + KeyNavigation.down: restartButton + } + + IconButton { + id: restartButton + iconName: "view-refresh" + text: i18n("Restart Lesson") + width: parent.width + onClicked: { + close() + restartRequested() + } + KeyNavigation.backtab: resumeButton + KeyNavigation.tab: returnButton + KeyNavigation.up: resumeButton + KeyNavigation.down: returnButton + } + + IconButton { + id: returnButton + iconName: "go-home" + text: i18n("Return to Home Screen") + width: parent.width + onClicked: { + close() + abortRequested() + } + KeyNavigation.backtab: restartButton + KeyNavigation.tab: resumeButton + KeyNavigation.up: restartButton + } + } + } +} + diff --git a/src/qml/trainingscreen/TrainingScreenMenuOverlay.qml b/src/qml/trainingscreen/TrainingScreenMenuOverlay.qml deleted file mode 100644 --- a/src/qml/trainingscreen/TrainingScreenMenuOverlay.qml +++ /dev/null @@ -1,182 +0,0 @@ -/* - * 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 1.3 -import QtQuick.Layouts 1.1 -import QtGraphicalEffects 1.0 -import ktouch 1.0 - -Loader { - id: loader - - property Item blurSource; - - signal closed() - signal restartRequested() - signal abortRequested() - - active: false - - function show() { - loader.active = true - } - - function hide() { - item.opacity = 0 - } - - sourceComponent: Component { - FocusScope { - id: item - anchors.fill: parent - - opacity: loader.status == Loader.Ready? 1: 0 - - Component.onCompleted: resumeButton.forceActiveFocus() - - Behavior on opacity { - SequentialAnimation { - NumberAnimation { - duration: 300 - easing.type: Easing.InOutQuad - } - ScriptAction { - script: { - if (opacity == 0) { - loader.active = false; - closed() - } - } - } - } - } - - - ShaderEffectSource { - id: effectSource - sourceItem: blurSource - anchors.fill: parent - hideSource: false - } - - HueSaturation { - id: desaturatedBackground - source: effectSource - anchors.fill: parent - lightness: -0.3 - saturation: -0.5 - visible: false - } - - FastBlur { - anchors.fill: parent - source: desaturatedBackground - radius: 50 - } - - Rectangle { - anchors.fill: parent - color: "#55000000" - } - - /* swallow all mouse events */ - MouseArea { - anchors.fill: parent - enabled: false - hoverEnabled: enabled - } - - - GroupBox { - id: groupBox - anchors.centerIn: parent - width: column.width + 30 - height: column.height + 30 - - Column { - id: column - focus: true - anchors.centerIn: parent - spacing: 15 - width: Math.max(resumeButton.implicitWidth, restartButton.implicitWidth, returnButton.implicitWidth) - - Button { - id: resumeButton - iconName: "go-next-view" - text: i18n("Resume Training") - width: parent.width - onClicked: hide() - KeyNavigation.backtab: returnButton - KeyNavigation.tab: restartButton - KeyNavigation.down: restartButton - } - - Button { - id: restartButton - iconName: "view-refresh" - text: i18n("Restart Lesson") - width: parent.width - onClicked: { - restartRequested() - hide() - } - KeyNavigation.backtab: resumeButton - KeyNavigation.tab: returnButton - KeyNavigation.up: resumeButton - KeyNavigation.down: returnButton - } - - Button { - id: returnButton - iconName: "go-home" - text: i18n("Return to Home Screen") - width: parent.width - onClicked: { - abortRequested() - hide() - } - KeyNavigation.backtab: restartButton - KeyNavigation.tab: resumeButton - KeyNavigation.up: restartButton - } - - Keys.onDownPressed: { - if (resumeButton.focus) - restartButton.focus = true; - else if (restartButton.focus) - returnButton.focus = true; - } - - Keys.onUpPressed: { - if (restartButton.focus) - resumeButton.focus = true; - else if (returnButton.focus) - restartButton.focus = true; - } - - Keys.onEscapePressed: { - hide() - } - } - } - } - - } -} - diff --git a/src/qml/trainingscreen/TrainingScreenToolbar.qml b/src/qml/trainingscreen/TrainingScreenToolbar.qml --- a/src/qml/trainingscreen/TrainingScreenToolbar.qml +++ b/src/qml/trainingscreen/TrainingScreenToolbar.qml @@ -1,6 +1,5 @@ /* - * Copyright 2012 Sebastian Gottfried - * Copyright 2015 Sebastian Gottfried + * 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 @@ -16,20 +15,21 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 -import QtQuick.Layouts 1.1 +import QtQuick 2.9 +import QtQuick.Layouts 1.3 import ktouch 1.0 import "../common" -Item { - id: item +ToolBar { + id: root property TrainingStats stats property bool trainingStarted: false property bool trainingFinished: true - property Item menuOverlayItem + property PopupDialog menu + + dimFactor: 1.8 function setMessage() { if (!stats.timeIsRunning && !trainingFinished) { @@ -62,16 +62,15 @@ right: parent.right leftMargin: 30 rightMargin: 30 - verticalCenterOffset: -1 } - spacing: 3 - height: menuButton.height + spacing: 5 - ToolButton { + IconToolButton { id: menuButton iconName: "go-home" - onClicked: item.menuOverlayItem.show() + onClicked: root.menu.open() + colorScheme: root.colorScheme } MessageBox { diff --git a/src/qml/trainingscreen/TrainingWidget.qml b/src/qml/trainingscreen/TrainingWidget.qml --- a/src/qml/trainingscreen/TrainingWidget.qml +++ b/src/qml/trainingscreen/TrainingWidget.qml @@ -1,5 +1,5 @@ /* - * Copyright 2013 Sebastian Gottfried + * 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 @@ -15,11 +15,11 @@ * along with this program. If not, see . */ -import QtQuick 2.4 -import QtQuick.Controls 1.3 +import QtQuick 2.9 import ktouch 1.0 import "../keyboard" +import "../common" FocusScope { id: trainingWidget diff --git a/src/undocommands/coursecommands.h b/src/undocommands/coursecommands.h --- a/src/undocommands/coursecommands.h +++ b/src/undocommands/coursecommands.h @@ -27,7 +27,7 @@ class SetCourseTitleCommand: public QUndoCommand { public: - SetCourseTitleCommand(Course* course, const QString& oldTitle, QUndoCommand* parent = 0); + SetCourseTitleCommand(Course* course, const QString& oldTitle, QUndoCommand* parent = nullptr); void undo() override; void redo() override; bool mergeWith(const QUndoCommand* command) override; @@ -41,7 +41,7 @@ class SetCourseKeyboadLayoutNameCommand: public QUndoCommand { public: - SetCourseKeyboadLayoutNameCommand(Course* course, const QString& oldKeyboardLayoutName, QUndoCommand* parent = 0); + SetCourseKeyboadLayoutNameCommand(Course* course, const QString& oldKeyboardLayoutName, QUndoCommand* parent = nullptr); void undo() override; void redo() override; bool mergeWith(const QUndoCommand* command) override; @@ -55,7 +55,7 @@ class SetCourseDescriptionCommand: public QUndoCommand { public: - SetCourseDescriptionCommand(Course* course, const QString& oldDescription, QUndoCommand* parent = 0); + SetCourseDescriptionCommand(Course* course, const QString& oldDescription, QUndoCommand* parent = nullptr); void undo() override; void redo() override; bool mergeWith(const QUndoCommand* command) override; @@ -69,7 +69,7 @@ class AddLessonCommand: public QUndoCommand { public: - AddLessonCommand(Course* course, int lessonIndex, const QString& lessonTitle, QUndoCommand* parent = 0); + AddLessonCommand(Course* course, int lessonIndex, const QString& lessonTitle, QUndoCommand* parent = nullptr); void undo() override; void redo() override; bool mergeWith(const QUndoCommand* command) override; @@ -83,7 +83,7 @@ class RemoveLessonCommand: public QUndoCommand { public: - RemoveLessonCommand(Course* course, int lessonIndex, QUndoCommand* parent = 0); + RemoveLessonCommand(Course* course, int lessonIndex, QUndoCommand* parent = nullptr); virtual ~RemoveLessonCommand(); void undo() override; void redo() override; @@ -98,7 +98,7 @@ class MoveLessonCommand: public QUndoCommand { public: - MoveLessonCommand(Course* course, int oldLessonIndex, int newLessonIndex, QUndoCommand* parent = 0); + MoveLessonCommand(Course* course, int oldLessonIndex, int newLessonIndex, QUndoCommand* parent = nullptr); void undo() override; void redo() override; bool mergeWith(const QUndoCommand* command) override; @@ -127,7 +127,7 @@ class SetLessonNewCharactersCommand: public QUndoCommand { public: - SetLessonNewCharactersCommand(Course* course, int lessonIndex, const QString& oldNewCharacters, QUndoCommand* parent = 0); + SetLessonNewCharactersCommand(Course* course, int lessonIndex, const QString& oldNewCharacters, QUndoCommand* parent = nullptr); void undo() override; void redo() override; bool mergeWith(const QUndoCommand* command) override; @@ -142,7 +142,7 @@ class SetLessonTextCommand: public QUndoCommand { public: - SetLessonTextCommand(Course* course, int lessonIndex, const QString& oldText, QUndoCommand* parent = 0); + SetLessonTextCommand(Course* course, int lessonIndex, const QString& oldText, QUndoCommand* parent = nullptr); void undo() override; void redo() override; bool mergeWith(const QUndoCommand* command) override; diff --git a/src/undocommands/keyboardlayoutcommands.h b/src/undocommands/keyboardlayoutcommands.h --- a/src/undocommands/keyboardlayoutcommands.h +++ b/src/undocommands/keyboardlayoutcommands.h @@ -29,7 +29,7 @@ class SetKeyboardLayoutTitleCommand : public QUndoCommand { public: - explicit SetKeyboardLayoutTitleCommand(KeyboardLayout* layout, const QString& newTitle, QUndoCommand* parent = 0); + explicit SetKeyboardLayoutTitleCommand(KeyboardLayout* layout, const QString& newTitle, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -43,7 +43,7 @@ class SetKeyboardLayoutNameCommand : public QUndoCommand { public: - explicit SetKeyboardLayoutNameCommand(KeyboardLayout* layout, const QString& newName, QUndoCommand* parent = 0); + explicit SetKeyboardLayoutNameCommand(KeyboardLayout* layout, const QString& newName, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -57,7 +57,7 @@ class SetKeyboardLayoutSizeCommand : public QUndoCommand { public: - explicit SetKeyboardLayoutSizeCommand(KeyboardLayout* layout, const QSize& newSize, QUndoCommand* parent = 0); + explicit SetKeyboardLayoutSizeCommand(KeyboardLayout* layout, const QSize& newSize, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -71,8 +71,8 @@ class AddKeyCommand : public QUndoCommand { public: - AddKeyCommand(KeyboardLayout* layout, AbstractKey* key, QUndoCommand* parent = 0); - ~AddKeyCommand(); + AddKeyCommand(KeyboardLayout* layout, AbstractKey* key, QUndoCommand* parent = nullptr); + ~AddKeyCommand() override; void undo() override; void redo() override; int id() const override; @@ -85,7 +85,7 @@ class RemoveKeyCommand : public QUndoCommand { public: - RemoveKeyCommand(KeyboardLayout* layout, int keyIndex, QUndoCommand* parent = 0); + RemoveKeyCommand(KeyboardLayout* layout, int keyIndex, QUndoCommand* parent = nullptr); ~RemoveKeyCommand(); void undo() override; void redo() override; @@ -100,7 +100,7 @@ class SetKeyGeometryCommand : public QUndoCommand { public: - explicit SetKeyGeometryCommand(KeyboardLayout* layout, int keyIndex, const QRect& newRect, QUndoCommand* parent = 0); + explicit SetKeyGeometryCommand(KeyboardLayout* layout, int keyIndex, const QRect& newRect, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -115,7 +115,7 @@ class SetKeyFingerIndexCommand : public QUndoCommand { public: - explicit SetKeyFingerIndexCommand(KeyboardLayout* layout, int keyIndex, int newFingerIndex, QUndoCommand* parent = 0); + explicit SetKeyFingerIndexCommand(KeyboardLayout* layout, int keyIndex, int newFingerIndex, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -130,7 +130,7 @@ class SetKeyHasHapticMarkerCommand : public QUndoCommand { public: - explicit SetKeyHasHapticMarkerCommand(KeyboardLayout* layout, int keyIndex, bool newHasHapticMarker, QUndoCommand* parent = 0); + explicit SetKeyHasHapticMarkerCommand(KeyboardLayout* layout, int keyIndex, bool newHasHapticMarker, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -145,7 +145,7 @@ class AddKeyCharCommand : public QUndoCommand { public: - AddKeyCharCommand(KeyboardLayout* layout, int keyIndex, QUndoCommand* parent = 0); + AddKeyCharCommand(KeyboardLayout* layout, int keyIndex, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -158,7 +158,7 @@ class RemoveKeyCharCommand : public QUndoCommand { public: - RemoveKeyCharCommand(KeyboardLayout* layout, int keyIndex, int keyCharIndex, QUndoCommand* parent = 0); + RemoveKeyCharCommand(KeyboardLayout* layout, int keyIndex, int keyCharIndex, QUndoCommand* parent = nullptr); ~RemoveKeyCharCommand(); void undo() override; void redo() override; @@ -174,7 +174,7 @@ class SetKeyCharValueCommand : public QUndoCommand { public: - explicit SetKeyCharValueCommand(KeyboardLayout* layout, int keyIndex, int keyCharIndex, QChar newValue, QUndoCommand* parent = 0); + explicit SetKeyCharValueCommand(KeyboardLayout* layout, int keyIndex, int keyCharIndex, QChar newValue, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -190,7 +190,7 @@ class SetKeyCharModifierCommand : public QUndoCommand { public: - explicit SetKeyCharModifierCommand(KeyboardLayout* layout, int keyIndex, int keyCharIndex, const QString& newModifier, QUndoCommand* parent = 0); + explicit SetKeyCharModifierCommand(KeyboardLayout* layout, int keyIndex, int keyCharIndex, const QString& newModifier, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -206,7 +206,7 @@ class SetKeyCharPositionCommand : public QUndoCommand { public: - explicit SetKeyCharPositionCommand(KeyboardLayout* layout, int keyIndex, int keyCharIndex, KeyChar::Position newPosition, QUndoCommand* parent = 0); + explicit SetKeyCharPositionCommand(KeyboardLayout* layout, int keyIndex, int keyCharIndex, KeyChar::Position newPosition, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -222,7 +222,7 @@ class SetSpecialKeyTypeCommand : public QUndoCommand { public: - explicit SetSpecialKeyTypeCommand(KeyboardLayout* layout, int keyIndex, SpecialKey::Type newType, QUndoCommand* parent = 0); + explicit SetSpecialKeyTypeCommand(KeyboardLayout* layout, int keyIndex, SpecialKey::Type newType, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -237,7 +237,7 @@ class SetSpecialKeyLabelCommand : public QUndoCommand { public: - explicit SetSpecialKeyLabelCommand(KeyboardLayout* layout, int keyIndex, const QString& newLabel, QUndoCommand* parent = 0); + explicit SetSpecialKeyLabelCommand(KeyboardLayout* layout, int keyIndex, const QString& newLabel, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override; @@ -252,7 +252,7 @@ class SetSpecialKeyModifierIdCommand : public QUndoCommand { public: - explicit SetSpecialKeyModifierIdCommand(KeyboardLayout* layout, int keyIndex, const QString& newModifiewId, QUndoCommand* parent = 0); + explicit SetSpecialKeyModifierIdCommand(KeyboardLayout* layout, int keyIndex, const QString& newModifiewId, QUndoCommand* parent = nullptr); void undo() override; void redo() override; int id() const override;