Commit eac69baa authored by Andreas Cord-Landwehr's avatar Andreas Cord-Landwehr
Browse files

Refactor welcome page and download integration

parent 63921a41
......@@ -28,33 +28,23 @@
DrawerTrainingActions::DrawerTrainingActions(QObject* parent)
: QObject(parent)
, m_course(nullptr)
{
}
void DrawerTrainingActions::setCourse(Course *course)
{
if (course == m_course) {
return;
}
m_course = course;
emit courseChanged(course);
updateActions();
}
Course * DrawerTrainingActions::course() const
{
return m_course;
}
void DrawerTrainingActions::setSession(TrainingSession *session)
{
if (session == m_session) {
return;
}
if (m_session) {
disconnect(m_session, &TrainingSession::courseChanged, this, &DrawerTrainingActions::updateActions);
}
m_session = session;
connect(m_session, &TrainingSession::courseChanged, this, &DrawerTrainingActions::updateActions);
emit sessionChanged();
updateActions();
}
TrainingSession * DrawerTrainingActions::session() const
......@@ -69,22 +59,34 @@ QList<QObject*> DrawerTrainingActions::actions() const
void DrawerTrainingActions::updateActions()
{
if (!m_session) {
return;
}
// cleanup
for (const auto &action : m_actions) {
action->deleteLater();
}
m_actions.clear();
if (!m_course) {
if (!m_session->course()) {
return;
}
for (const auto &unit : m_course->unitList()) {
for (const auto &unit : m_session->course()->unitList()) {
auto action = new TrainingAction(unit->title());
m_actions.append(action);
for (const auto &phrase : unit->phraseList()) {
if (phrase->sound().isEmpty()) {
continue;
}
action->appendChild(new TrainingAction(phrase, this, unit));
}
if (action->hasChildren()) {
m_actions.append(action);
} else {
action->deleteLater();
}
}
emit actionsChanged();
}
......@@ -30,23 +30,19 @@ class Course;
class ARTIKULATECORE_EXPORT DrawerTrainingActions : public QObject
{
Q_OBJECT
Q_PROPERTY(Course *course READ course WRITE setCourse NOTIFY courseChanged)
Q_PROPERTY(TrainingSession *session READ session WRITE setSession NOTIFY sessionChanged)
Q_PROPERTY(QList<QObject*> actions READ actions NOTIFY actionsChanged)
public:
DrawerTrainingActions(QObject *parent = nullptr);
void setCourse(Course *course);
Course * course() const;
void setSession(TrainingSession *session);
TrainingSession * session() const;
QList<QObject*> actions() const;
private:
private Q_SLOTS:
void updateActions();
Q_SIGNALS:
void courseChanged(Course *course);
void actionsChanged();
void sessionChanged();
/**
......@@ -55,7 +51,6 @@ Q_SIGNALS:
void triggerTrainingView();
private:
Course *m_course{nullptr};
TrainingSession *m_session{nullptr};
QList<QObject *> m_actions;
};
......
......@@ -53,6 +53,8 @@ ResourceManager::ResourceManager(QObject *parent)
void ResourceManager::loadCourseResources()
{
//TODO fix this method such that it may be called many times of e.g. updating
// reload config, could be changed in dialogs
Settings::self()->load();
......@@ -236,11 +238,14 @@ Language * ResourceManager::language(LearnerProfile::LearningGoal *learningGoal)
QList< CourseResource* > ResourceManager::courseResources(Language *language)
{
Q_ASSERT(language);
if (!language) {
return QList< CourseResource* >();
QList<CourseResource *> courses;
for (auto iter = m_courseResources.constBegin(); iter != m_courseResources.constEnd(); ++iter) {
courses.append(iter.value());
}
return courses;
}
// return empty list if no course available
// return empty list if no course available for language
if (!m_courseResources.contains(language->id())) {
return QList< CourseResource* >();
}
......
......@@ -49,8 +49,8 @@ namespace LearnerProfile {
class ARTIKULATECORE_EXPORT ResourceManager : public QObject
{
Q_OBJECT
Q_PROPERTY(bool isRepositoryManager READ isRepositoryManager NOTIFY repositoryChanged);
Q_PROPERTY(QString repositoryUrl READ repositoryUrl NOTIFY repositoryChanged);
Q_PROPERTY(bool isRepositoryManager READ isRepositoryManager NOTIFY repositoryChanged)
Q_PROPERTY(QString repositoryUrl READ repositoryUrl NOTIFY repositoryChanged)
public:
explicit ResourceManager(QObject *parent = nullptr);
......@@ -59,8 +59,10 @@ public:
* Load all course resources.
* This loading is very fast, since course files are only partly (~20 top lines) parsed and
* the complete parsing is postproned until first access.
*
* This method is safe to be called several times for incremental updates.
*/
void loadCourseResources();
Q_INVOKABLE void loadCourseResources();
/**
* This method loads all language files that are provided in the standard directories
......
......@@ -54,6 +54,11 @@ void TrainingAction::appendChild(QObject* child)
emit childrenChanged();
}
bool TrainingAction::hasChildren() const
{
return m_children.count() > 0;
}
void TrainingAction::trigger()
{
if (m_phrase && m_drawerTrainingActions && m_drawerTrainingActions->session()) {
......
......@@ -52,6 +52,7 @@ public:
TrainingAction(const QString &text, QObject *parent = nullptr);
TrainingAction(Phrase *phrase, DrawerTrainingActions *drawerActions, QObject *parent = nullptr);
void appendChild(QObject *child);
bool hasChildren() const;
Q_INVOKABLE void trigger();
bool enabled() const;
void setEnabled(bool enabled);
......
......@@ -31,7 +31,6 @@
TrainingSession::TrainingSession(QObject *parent)
: QObject(parent)
, m_profileManager(nullptr)
, m_language(nullptr)
, m_course(nullptr)
, m_unit(nullptr)
, m_phrase(nullptr)
......@@ -47,20 +46,6 @@ void TrainingSession::setProfileManager(LearnerProfile::ProfileManager *manager)
m_profileManager = manager;
}
Language * TrainingSession::language() const
{
return m_language;
}
void TrainingSession::setLanguage(Language *language)
{
if (m_language == language) {
return;
}
m_language = language;
emit languageChanged();
}
Course * TrainingSession::course() const
{
return m_course;
......@@ -75,32 +60,32 @@ void TrainingSession::setCourse(Course *course)
return;
}
m_course = course;
if (m_course && m_course->unitList().count() > 0) {
setUnit(m_course->unitList().first());
}
// lazy loading of training data
LearnerProfile::LearningGoal * goal = m_profileManager->goal(
LearnerProfile::LearningGoal::Language, m_course->id());
if (!goal) {
goal = m_profileManager->registerGoal(
LearnerProfile::LearningGoal::Language,
course->language()->id(),
course->language()->i18nTitle()
);
}
auto data = m_profileManager->progressValues(m_profileManager->activeProfile(),
goal,
m_course->id()
);
Q_FOREACH(Unit *unit, m_course->unitList()) {
Q_FOREACH(Phrase *phrase, unit->phraseList()) {
auto iter = data.find(phrase->id());
if (iter != data.end()) {
phrase->setProgress(iter.value());
}
}
}
// if (m_course && m_course->unitList().count() > 0) {
// setUnit(m_course->unitList().first());
// }
// // lazy loading of training data
// LearnerProfile::LearningGoal * goal = m_profileManager->goal(
// LearnerProfile::LearningGoal::Language, m_course->id());
// if (!goal) {
// goal = m_profileManager->registerGoal(
// LearnerProfile::LearningGoal::Language,
// course->language()->id(),
// course->language()->i18nTitle()
// );
// }
// auto data = m_profileManager->progressValues(m_profileManager->activeProfile(),
// goal,
// m_course->id()
// );
// Q_FOREACH(Unit *unit, m_course->unitList()) {
// Q_FOREACH(Phrase *phrase, unit->phraseList()) {
// auto iter = data.find(phrase->id());
// if (iter != data.end()) {
// phrase->setProgress(iter.value());
// }
// }
// }
emit courseChanged();
}
......
......@@ -41,7 +41,6 @@ namespace LearnerProfile {
class ARTIKULATECORE_EXPORT TrainingSession : public QObject
{
Q_OBJECT
Q_PROPERTY(Language *language READ language WRITE setLanguage NOTIFY languageChanged)
Q_PROPERTY(Course *course READ course WRITE setCourse NOTIFY courseChanged)
Q_PROPERTY(Unit *unit READ unit WRITE setUnit NOTIFY unitChanged)
Q_PROPERTY(Phrase *phrase READ phrase WRITE setPhrase NOTIFY phraseChanged)
......@@ -51,8 +50,6 @@ public:
explicit TrainingSession(QObject *parent = nullptr);
void setProfileManager(LearnerProfile::ProfileManager *manager);
Language * language() const;
void setLanguage(Language *language);
Course * course() const;
void setCourse(Course *course);
Unit * unit() const;
......@@ -69,17 +66,15 @@ public:
Q_INVOKABLE void skipPhrase();
Q_SIGNALS:
void languageChanged();
void courseChanged();
void unitChanged();
void phraseChanged();
private:;
private:
Q_DISABLE_COPY(TrainingSession)
Phrase * nextPhrase() const;
void updateGoal();
LearnerProfile::ProfileManager *m_profileManager;
Language *m_language;
Course *m_course;
Unit *m_unit;
Phrase *m_phrase;
......
......@@ -42,17 +42,13 @@ CourseModel::CourseModel(QObject *parent)
this, &CourseModel::rowCountChanged);
}
CourseModel::~CourseModel()
{
}
QHash< int, QByteArray > CourseModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[TitleRole] = "title";
roles[DescriptionRole] = "description";
roles[IdRole] = "id";
roles[LanguageRole] = "language";
roles[DataRole] = "dataRole";
return roles;
......@@ -73,14 +69,14 @@ void CourseModel::setResourceManager(ResourceManager *resourceManager)
m_resourceManager = resourceManager;
m_resources.clear();
if (m_resourceManager) {
connect(m_resourceManager, SIGNAL(courseResourceAboutToBeAdded(CourseResource*,int)),
SLOT(onCourseResourceAboutToBeAdded(CourseResource*,int)));
connect(m_resourceManager, SIGNAL(courseResourceAdded()),
SLOT(onCourseResourceAdded()));
connect(m_resourceManager, SIGNAL(courseResourceAboutToBeRemoved(int)),
SLOT(onCourseResourceAboutToBeRemoved(int)));
connect(m_resourceManager, &ResourceManager::courseResourceAboutToBeAdded,
this, &CourseModel::onCourseResourceAboutToBeAdded);
connect(m_resourceManager, &ResourceManager::courseResourceAdded,
this, &CourseModel::onCourseResourceAdded);
connect(m_resourceManager, &ResourceManager::courseResourceAboutToBeRemoved,
this, &CourseModel::onCourseResourceAboutToBeRemoved);
}
if (m_language && m_resourceManager) {
if (m_resourceManager) {
m_resources = m_resourceManager->courseResources(m_language);
}
endResetModel();
......@@ -101,10 +97,7 @@ void CourseModel::setLanguage(Language *language)
{
emit beginResetModel();
m_language = language;
m_resources.clear();
if (m_language) {
m_resources = m_resourceManager->courseResources(m_language);
}
m_resources = m_resourceManager->courseResources(m_language);
emit languageChanged();
emit endResetModel();
emit rowCountChanged();
......@@ -137,6 +130,8 @@ QVariant CourseModel::data(const QModelIndex& index, int role) const
return course->id();
case ContributerResourceRole:
return m_resources.at(index.row())->isContributorResource();
case LanguageRole:
return QVariant::fromValue<QObject*>(course->language());
case DataRole:
return QVariant::fromValue<QObject*>(course);
default:
......@@ -146,9 +141,6 @@ QVariant CourseModel::data(const QModelIndex& index, int role) const
int CourseModel::rowCount(const QModelIndex& parent) const
{
if (!m_language) {
return 0;
}
if (parent.isValid()) {
return 0;
}
......@@ -157,6 +149,7 @@ int CourseModel::rowCount(const QModelIndex& parent) const
void CourseModel::onCourseResourceAboutToBeAdded(CourseResource *resource, int index)
{
Q_UNUSED(index);
beginInsertRows(QModelIndex(), m_resources.count(), m_resources.count());
m_resources.append(resource);
......@@ -173,7 +166,7 @@ void CourseModel::onCourseResourceAdded()
void CourseModel::onCourseResourceAboutToBeRemoved(int index)
{
if (!m_language) {
if (index >= m_resourceManager->courseResources(m_language).count()) {
return;
}
CourseResource *originalResource = m_resourceManager->courseResources(m_language).at(index);
......@@ -208,10 +201,6 @@ QVariant CourseModel::headerData(int section, Qt::Orientation orientation, int r
void CourseModel::updateMappings()
{
if (!m_language) {
qCDebug(ARTIKULATE_LOG) << "Aborting to update mappings, language not set.";
return;
}
int courses = m_resources.count();
for (int i = 0; i < courses; i++) {
m_signalMapper->setMapping(m_resources.at(i)->course(), i);
......
......@@ -43,22 +43,23 @@ public:
DescriptionRole,
IdRole,
ContributerResourceRole,
LanguageRole,
DataRole
};
explicit CourseModel(QObject *parent = nullptr);
virtual ~CourseModel();
~CourseModel() override = default;
/**
* Reimplemented from QAbstractListModel::roleNames()
*/
virtual QHash<int,QByteArray> roleNames() const Q_DECL_OVERRIDE;
QHash<int,QByteArray> roleNames() const override;
void setResourceManager(ResourceManager *resourceManager);
ResourceManager * resourceManager() const;
void setLanguage(Language *language);
Language * language() const;
virtual QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
virtual int rowCount(const QModelIndex &parent = QModelIndex()) const Q_DECL_OVERRIDE;
virtual QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const Q_DECL_OVERRIDE;
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;
Q_INVOKABLE QVariant course(int index) const;
Q_SIGNALS:
......
......@@ -63,7 +63,6 @@ Kirigami.GlobalDrawer {
actions: trainingActions.actions
DrawerTrainingActions {
id: trainingActions
course: g_trainingSession.course
session: g_trainingSession
onTriggerTrainingView: {
root.pageStack.clear();
......
......@@ -36,6 +36,10 @@ Kirigami.Page {
height: 50
width: parent.width
text: model.name
readonly property var status: model.status
onStatusChanged: {
g_resourceManager.loadCourseResources();
}
checkable: false
RowLayout {
id: layout
......
/*
* Copyright 2013-2015 Andreas Cord-Landwehr <cordlandwehr@kde.org>
*
* 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) version 3 or any later version
* accepted by the membership of KDE e.V. (or its successor approved
* by the membership of KDE e.V.), which shall act as a proxy
* defined in Section 14 of version 3 of the license.
*
* 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 <http://www.gnu.org/licenses/>.
*/
import QtQuick 2.1
import QtQuick.Controls 1.2
import artikulate 1.0
Item {
id: root
property Language selectedLanguage
property ResourceManager resourceManager
signal languageSelected(variant language)
width: 300
height: Math.max(buttonLeft.height, languageView.height)
// emit language selection
Connections {
target: buttonRight
onClicked: {
selectedLanguage = languageModel.language(languageView.currentIndex)
languageSelected(selectedLanguage)
}
}
Connections {
target: buttonLeft
onClicked: {
selectedLanguage = languageModel.language(languageView.currentIndex)
languageSelected(selectedLanguage)
}
}
// react on changed goals
Component.onCompleted: {
for (var i = 0; i < languageView.count; ++i) {
if (g_trainingSession.language.id == languageModel.language(i).id) {
languageView.currentIndex = i
}
}
}
Component {
id: itemDelegate
Row {
id: languageInfo
property Language language: model.dataRole
width: root.width - buttonLeft.width - buttonRight.width - 20
height: theme.mediumIconSize
spacing: 10
Icon {
id: icon
icon: "artikulate-language"
width: theme.mediumIconSize
height: theme.mediumIconSize
anchors.verticalCenter: parent.verticalCenter
}
Label {
id: languageTitleLabel
anchors.verticalCenter: parent.verticalCenter
height: paintedHeight
font.pointSize: 1.5 * theme.fontPointSize
text: language != null ? language.title + " / " + language.i18nTitle : ""
}
}
}
ListView {
id: languageView
width: root.width - buttonLeft.width - buttonRight.width - 20
height: theme.mediumIconSize
clip: true
snapMode: ListView.SnapToItem
orientation: ListView.Vertical
model: LanguageModel {
id: languageModel
view: LanguageModel.NonEmptyGhnsOnlyLanguages
resourceModel: LanguageResourceModel { resourceManager: root.resourceManager }
}
delegate: itemDelegate
}
Row {
visible: languageView.count == 0
spacing: 10
anchors {
left: languageView.left
top: languageView.top
}
Icon {
id: icon
icon: "dialog-information"
width: theme.mediumIconSize
height: theme.mediumIconSize
anchors.verticalCenter: parent.verticalCenter
}
Label {
id: favoritesUnsetInformation
anchors.verticalCenter: parent.verticalCenter
height: paintedHeight
font.pointSize: 1.5 * theme.fontPointSize
text: i18n("Please download a course") + languageModel.rows
}
}
ToolButton {
id: buttonLeft
anchors {
left: languageView.right
leftMargin: 10
top: languageView.top
}
iconName: "arrow-left"
enabled: languageView.currentIndex > 0 && languageView.count > 0
onClicked: {
languageView.decrementCurrentIndex()
}
}
ToolButton {
id : buttonRight
anchors {
left: buttonLeft.right
leftMargin: 10
top: languageView.top
}
enabled: languageView.currentIndex < languageView.count - 1 && languageView.count > 0
iconName: "arrow-right"
onClicked: {
languageView.incrementCurrentIndex()
}
}
}
/*
* Copyright 2015-2017 Andreas Cord-Landwehr <cordlandwehr@kde.org>
* Copyright 2015-2019 Andreas Cord-Landwehr <cordlandwehr@kde.org>
*
* This program is free software; you can redistribute it and/or