diff --git a/src/main_editor.cpp b/src/main_editor.cpp index 112520e..04932f2 100644 --- a/src/main_editor.cpp +++ b/src/main_editor.cpp @@ -1,61 +1,58 @@ /* * Copyright 2015 Andreas Cord-Landwehr * * 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 . */ #include "version.h" #include "application.h" #include "core/contributorrepository.h" #include "mainwindow_editor.h" #include #include #include #include "artikulate_debug.h" int main(int argc, char **argv) { Application app(argc, argv); KLocalizedString::setApplicationDomain("artikulate"); ContributorRepository repository; repository.reloadCourses(); app.installResourceRepository(&repository); KAboutData aboutData(QStringLiteral("artikulate_editor"), ki18nc("@title Displayed program name", "Artikulate Editor").toString(), ARTIKULATE_VERSION_STRING, ki18nc("@title KAboutData: short program description", "Artikulate Course Editor").toString(), KAboutLicense::GPL_V2, ki18nc("@info:credit", "(c) 2013-2019 The Artikulate Developers").toString(), ki18nc("@title Short program description", "Edit Artikulate course files.").toString() ); aboutData.addAuthor(ki18nc("@info:credit Developer name", "Andreas Cord-Landwehr").toString(), ki18nc("@info:credit Role", "Original Author").toString(), QStringLiteral("cordlandwehr@kde.org")); KAboutData::setApplicationData(aboutData); KCrash::initialize(); MainWindowEditor *mainWindow = new MainWindowEditor(&repository); - QSize size(800, 600); - mainWindow->setMinimumSize(size); - mainWindow->show(); return app.exec(); } diff --git a/src/mainwindow_editor.cpp b/src/mainwindow_editor.cpp index d640a11..580d4f7 100644 --- a/src/mainwindow_editor.cpp +++ b/src/mainwindow_editor.cpp @@ -1,198 +1,172 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 . */ #include "mainwindow_editor.h" #include "application.h" #include "ui/resourcesdialogpage.h" #include "ui/sounddevicedialogpage.h" #include "ui/appearencedialogpage.h" #include "ui/exportghnsdialog.h" #include "core/editorsession.h" #include "core/resources/courseresource.h" #include "models/languagemodel.h" #include "settings.h" #include "libsound/src/outputdevicecontroller.h" #include #include #include #include -#include -#include #include +#include #include #include #include #include #include "artikulate_debug.h" #include #include #include #include #include #include #include #include #include #include using namespace LearnerProfile; MainWindowEditor::MainWindowEditor(ContributorRepository *repository) : m_repository(repository) , m_editorSession(new EditorSession()) - , m_widget(new QQuickWidget) { + rootContext()->setContextObject(new KLocalizedContext(this)); + rootContext()->setContextProperty(QStringLiteral("g_repository"), m_repository); + rootContext()->setContextProperty(QStringLiteral("g_editorSession"), m_editorSession); + rootContext()->setContextProperty(QStringLiteral("g_artikulateAboutData"), QVariant::fromValue(KAboutData::applicationData())); + m_repository->setStorageLocation(Settings::courseRepositoryPath()); m_editorSession->setRepository(m_repository); - setWindowIcon(QIcon::fromTheme(QStringLiteral("artikulate"))); - setWindowTitle(qAppName()); - setAutoSaveSettings(); - - // workaround for QTBUG-40765 - qApp->setAttribute(Qt::AA_DontCreateNativeWidgetSiblings); // load saved sound settings OutputDeviceController::self().setVolume(Settings::audioOutputVolume()); - // load resources - if (m_repository->languages().isEmpty()) { - qFatal("No language resources found, cannot start application."); - } m_repository->reloadCourses(); // create menu setupActions(); - // set view - m_widget->resize(QSize(800, 600)); - m_widget->rootContext()->setContextObject(new KLocalizedContext(m_widget)); - m_widget->rootContext()->setContextProperty(QStringLiteral("g_repository"), m_repository); - m_widget->rootContext()->setContextProperty(QStringLiteral("g_editorSession"), m_editorSession); - // set starting screen - m_widget->setSource(QUrl(QStringLiteral("qrc:/artikulate/qml/Editor.qml"))); - m_widget->setResizeMode(QQuickWidget::SizeRootObjectToView); - - QAction *newAct = KStandardAction::save(this, SLOT(save()), actionCollection()); - actionCollection()->addAction(QStringLiteral("save"), newAct); - - // set status bar - statusBar()->setEnabled(true); - QLabel *repositoryLabel = new QLabel; - repositoryLabel->setText(i18n("Course Repository: %1", m_repository->storageLocation())); - connect(m_repository, &ContributorRepository::repositoryChanged, this, [=]() { - repositoryLabel->setText(i18n("Course Repository: %1", m_repository->storageLocation())); - }); - statusBar()->insertWidget(0, repositoryLabel); - - createGUI(QStringLiteral("artikulateui_editor.rc")); - setCentralWidget(m_widget); + load(QUrl(QStringLiteral("qrc:/artikulate/qml/Editor.qml"))); + +// QAction *newAct = KStandardAction::save(this, SLOT(save()), actionCollection()); +// actionCollection()->addAction(QStringLiteral("save"), newAct); } MainWindowEditor::~MainWindowEditor() { // save current settings for case of closing Settings::self()->save(); } ContributorRepository * MainWindowEditor::resourceRepository() const { return m_repository; } void MainWindowEditor::setupActions() { - QAction *settingsAction = new QAction(i18nc("@item:inmenu", "Configure Artikulate"), this); - connect(settingsAction, &QAction::triggered, this, &MainWindowEditor::showSettingsDialog); - actionCollection()->addAction(QStringLiteral("settings"), settingsAction); - settingsAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); - - QAction *exportAction = new QAction(i18nc("@item:inmenu", "Export GHNS Files"), this); - connect(exportAction, &QAction::triggered, this, [=]() { - QPointer dialog = new ExportGhnsDialog(m_repository); - dialog->exec(); - }); - actionCollection()->addAction(QStringLiteral("export_ghns"), exportAction); - exportAction->setIcon(QIcon::fromTheme(QStringLiteral("document-export"))); - - KStandardAction::quit(this, SLOT(quit()), actionCollection()); - - setupGUI(Keys | Save | Create, QStringLiteral("artikulateui_editor.rc")); +// QAction *settingsAction = new QAction(i18nc("@item:inmenu", "Configure Artikulate"), this); +// connect(settingsAction, &QAction::triggered, this, &MainWindowEditor::showSettingsDialog); +// actionCollection()->addAction(QStringLiteral("settings"), settingsAction); +// settingsAction->setIcon(QIcon::fromTheme(QStringLiteral("configure"))); + +// QAction *exportAction = new QAction(i18nc("@item:inmenu", "Export GHNS Files"), this); +// connect(exportAction, &QAction::triggered, this, [=]() { +// QPointer dialog = new ExportGhnsDialog(m_repository); +// dialog->exec(); +// }); +// actionCollection()->addAction(QStringLiteral("export_ghns"), exportAction); +// exportAction->setIcon(QIcon::fromTheme(QStringLiteral("document-export"))); + +// KStandardAction::quit(this, SLOT(quit()), actionCollection()); + +// setupGUI(Keys | Save | Create, QStringLiteral("artikulateui_editor.rc")); } void MainWindowEditor::showSettingsDialog() { if (KConfigDialog::showDialog(QStringLiteral("settings"))) { return; } QPointer dialog = new KConfigDialog(nullptr, QStringLiteral("settings"), Settings::self()); ResourcesDialogPage *resourceDialog = new ResourcesDialogPage(m_repository); SoundDeviceDialogPage *soundDialog = new SoundDeviceDialogPage(); AppearenceDialogPage *appearenceDialog = new AppearenceDialogPage(); resourceDialog->loadSettings(); soundDialog->loadSettings(); appearenceDialog->loadSettings(); dialog->addPage(soundDialog, i18nc("@item:inmenu", "Sound Devices"), QStringLiteral("audio-headset"), i18nc("@title:tab", "Sound Device Settings"), true); dialog->addPage(appearenceDialog, i18nc("@item:inmenu", "Fonts"), QStringLiteral("preferences-desktop-font"), i18nc("@title:tab", "Training Phrase Font"), true); dialog->addPage(resourceDialog, i18nc("@item:inmenu", "Course Resources"), QStringLiteral("repository"), i18nc("@title:tab", "Resource Repository Settings"), true); connect(dialog.data(), &QDialog::accepted, resourceDialog, &ResourcesDialogPage::saveSettings); connect(dialog.data(), &QDialog::accepted, soundDialog, &SoundDeviceDialogPage::saveSettings); connect(dialog.data(), &QDialog::accepted, appearenceDialog, &AppearenceDialogPage::saveSettings); dialog->exec(); } void MainWindowEditor::save() { m_repository->sync(); } void MainWindowEditor::quit() { - if (queryClose()) { - qApp->quit(); - } +// if (queryClose()) { +// qApp->quit(); +// } } -bool MainWindowEditor::queryClose() -{ - if (!m_repository->modified()) { - return true; - } - - int result = KMessageBox::warningYesNoCancel(nullptr, i18nc("@info", - "The currently open course contains unsaved changes. Do you want to save them?")); - - switch(result) { - case KMessageBox::Yes: - m_repository->sync(); - return true; - case KMessageBox::No: - return true; - default: - return false; - } -} +//bool MainWindowEditor::queryClose() +//{ +// if (!m_repository->modified()) { +// return true; +// } + +// int result = KMessageBox::warningYesNoCancel(nullptr, i18nc("@info", +// "The currently open course contains unsaved changes. Do you want to save them?")); + +// switch(result) { +// case KMessageBox::Yes: +// m_repository->sync(); +// return true; +// case KMessageBox::No: +// return true; +// default: +// return false; +// } +//} diff --git a/src/mainwindow_editor.h b/src/mainwindow_editor.h index 0f16c5f..fbf6dcb 100644 --- a/src/mainwindow_editor.h +++ b/src/mainwindow_editor.h @@ -1,70 +1,64 @@ /* * Copyright 2015 Andreas Cord-Landwehr * * 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 . */ #ifndef MAINWINDOW_EDITOR_H #define MAINWINDOW_EDITOR_H -#include +#include #include #include "core/contributorrepository.h" #include "core/trainingsession.h" class EditorSession; class QQuickWidget; -class MainWindowEditor : public KXmlGuiWindow +class MainWindowEditor : public QQmlApplicationEngine { Q_OBJECT public: - /** * Default Constructor */ MainWindowEditor(ContributorRepository *repository); /** * Default Destructor */ virtual ~MainWindowEditor(); ContributorRepository * resourceRepository() const; void setupActions(); - QSize sizeHint() const override { return QSize(1000, 700); } - - bool queryClose() override; - public Q_SLOTS: void showSettingsDialog(); void save(); void quit(); Q_SIGNALS: void modeChanged(bool); private: ContributorRepository *m_repository; EditorSession *m_editorSession; - QQuickWidget *m_widget; }; #endif diff --git a/src/qml/ActionListItem.qml b/src/qml/ActionListItem.qml index 799b45b..f88b2ff 100644 --- a/src/qml/ActionListItem.qml +++ b/src/qml/ActionListItem.qml @@ -1,46 +1,46 @@ /*************************************************************************** * Copyright © 2015 Aleix Pol Gonzalez * * * * 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 . * ***************************************************************************/ import QtQuick 2.5 import QtQuick.Controls 2.0 -import org.kde.kirigami 2.3 as Kirigami +import org.kde.kirigami 2.7 as Kirigami Kirigami.BasicListItem { id: item property QtObject action: null checked: action.checked icon: action.iconName separatorVisible: false visible: action.enabled onClicked: { action.trigger() } Kirigami.MnemonicData.enabled: item.enabled && item.visible Kirigami.MnemonicData.controlType: Kirigami.MnemonicData.MenuItem Kirigami.MnemonicData.label: action.text label: Kirigami.MnemonicData.richTextLabel readonly property var p0: Shortcut { sequence: item.Kirigami.MnemonicData.sequence onActivated: item.clicked() } } diff --git a/src/qml/ArtikulateDrawer.qml b/src/qml/ArtikulateDrawer.qml index e55b5e9..8198e83 100644 --- a/src/qml/ArtikulateDrawer.qml +++ b/src/qml/ArtikulateDrawer.qml @@ -1,154 +1,154 @@ /* * Copyright 2018 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.5 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.0 as QQC2 -import org.kde.kirigami 2.0 as Kirigami +import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 Kirigami.GlobalDrawer { id: root title: "Artikulate" titleIcon: "artikulate" resetMenuOnTriggered: false bottomPadding: 0 property QtObject pageStack // enforce drawer always to be open modal: false handleVisible: false topContent: [ ColumnLayout { spacing: 0 Layout.fillWidth: true Layout.leftMargin: -root.leftPadding Layout.rightMargin: -root.rightPadding ActionListItem { action: Kirigami.Action { text: i18n("Training") iconName: "artikulate" onTriggered: { root.pageStack.clear(); root.pageStack.push(welcomePageComponent); } } } Kirigami.Separator { Layout.fillWidth: true } } ] // ordinary Kirigami actions are filled from training units/phrases actions: trainingActions.actions DrawerTrainingActions { id: trainingActions session: g_trainingSession onTriggerTrainingView: { root.pageStack.clear(); root.pageStack.push(trainingPageComponent); } } //TODO integrate again // [ // Kirigami.Action { // text: i18n("Help") // iconName: "help-about" // Kirigami.Action { // text: i18n("Artikulate Handbook") // iconName: "help-contents" // onTriggered: { // triggerAction("help_contents"); // globalDrawer.resetMenu(); // } // } // Kirigami.Action { // text: i18n("Report Bug") // iconName: "tools-report-bug" // onTriggered: { // triggerAction("help_report_bug"); // globalDrawer.resetMenu(); // } // } // Kirigami.Action { // text: i18n("About KDE") // iconName: "help-about" // onTriggered: { // triggerAction("help_about_kde") // globalDrawer.resetMenu(); // } // } // } // ] ColumnLayout { spacing: 0 Layout.fillWidth: true Layout.leftMargin: -root.leftPadding Layout.rightMargin: -root.rightPadding Kirigami.Separator { Layout.fillWidth: true } //TODO currently disabled while contents have to be ported // ActionListItem { // action: Kirigami.Action { // text: i18n("Statistics") // iconName: "user-properties" // onTriggered: { // root.pageStack.pop(); // root.pageStack.push(profileSettingsPageComponent); // } // } // } // ActionListItem { // action: Kirigami.Action { // text: i18n("Settings") // iconName: "settings-configure" // onTriggered: triggerSettingsDialog() // } // } ActionListItem { action: Kirigami.Action { text: i18n("Download Training") iconName: "get-hot-new-stuff" onTriggered: { root.pageStack.pop(); root.pageStack.push(downloadPageComponent); } } } ActionListItem { action: Kirigami.Action { text: i18n("About") iconName: "help-about" onTriggered: { root.pageStack.pop(); root.pageStack.push(aboutPageComponent); } } } } } diff --git a/src/qml/DownloadPage.qml b/src/qml/DownloadPage.qml index d538cea..d7689f9 100644 --- a/src/qml/DownloadPage.qml +++ b/src/qml/DownloadPage.qml @@ -1,128 +1,128 @@ /* * Copyright 2018 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.1 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.1 as QQC2 -import org.kde.kirigami 2.0 as Kirigami +import org.kde.kirigami 2.7 as Kirigami import org.kde.newstuff 1.0 as KNS Kirigami.Page { id: root /** * emitted whenever GHNS resources changed */ signal statusChanged(); title: i18n("Download Training Material") background: Rectangle { color: "#ffffff" } Component { id: courseDownloadItem Kirigami.AbstractListItem { id: listItem height: 50 width: parent.width text: model.name readonly property var status: model.status onStatusChanged: { root.statusChanged(); } checkable: false RowLayout { id: layout spacing: Kirigami.Units.smallSpacing*2 Kirigami.Icon { height: Kirigami.Units.iconSizes.smallMedium; width: height; SequentialAnimation on opacity { loops: Animation.Infinite; running: model.status == KNS.ItemsModel.InstallingStatus || model.status == KNS.ItemsModel.UpdatingStatus NumberAnimation { to: 0; duration: 500; } NumberAnimation { to: 1; duration: 500; } onRunningChanged: { if (!running) parent.opacity = 1; } } source: { // use complete list of KNS status messages if (model.status == KNS.ItemsModel.InvalidStatus) return "emblem-error"; if (model.status == KNS.ItemsModel.DownloadableStatus) return "vcs-added"; if (model.status == KNS.ItemsModel.InstalledStatus) return "vcs-normal"; if (model.status == KNS.ItemsModel.UpdateableStatus) return "vcs-update-required"; if (model.status == KNS.ItemsModel.DeletedStatus) return "vcs-added"; if (model.status == KNS.ItemsModel.InstallingStatus) return "vcs-locally-modified"; if (model.status == KNS.ItemsModel.UpdatingStatus) return "vcs-locally-modified"; return "emblem-error"; } } QQC2.Label { id: labelItem Layout.fillWidth: true text: listItem.text color: layout.indicateActiveFocus && (listItem.highlighted || listItem.checked || listItem.pressed) ? listItem.activeTextColor : listItem.textColor elide: Text.ElideRight font: listItem.font } QQC2.Button { visible: (model.status == KNS.ItemsModel.UpdateableStatus) ? true : false; text: i18nc("@action:button", "Update") onClicked: newStuffModel.installItem(model.index) } QQC2.Button { visible: (model.status == KNS.ItemsModel.DownloadableStatus || model.status == KNS.ItemsModel.DeletedStatus) ? true : false; text: i18nc("@action:button", "Install") onClicked: newStuffModel.installItem(model.index) } QQC2.Button { visible: (model.status == KNS.ItemsModel.InstalledStatus || model.status == KNS.ItemsModel.UpdateableStatus) ? true : false; text: i18nc("@action:button", "Remove") onClicked: newStuffModel.uninstallItem(model.index) } } } } ColumnLayout { ListView { id: listView width: root.width - 40 height: 50 * listView.count delegate: courseDownloadItem model: KNS.ItemsModel { id: newStuffModel; engine: newStuffEngine.engine; } KNS.Engine { id: newStuffEngine; configFile: ":/artikulate/config/artikulate.knsrc"; onMessage: console.log("KNS Message: " + message); onIdleMessage: console.log("KNS Idle: " + message); onBusyMessage: console.log("KNS Busy: " + message); onErrorMessage: console.log("KNS Error: " + message); } } } } diff --git a/src/qml/Editor.qml b/src/qml/EditCoursePage.qml similarity index 53% copy from src/qml/Editor.qml copy to src/qml/EditCoursePage.qml index 0e02ae4..cfaf10f 100644 --- a/src/qml/Editor.qml +++ b/src/qml/EditCoursePage.qml @@ -1,248 +1,195 @@ /* * Copyright 2013-2019 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.5 import QtQuick.Controls 2.3 -import QtQuick.Controls 1.4 as QQC1 import QtQuick.Layouts 1.2 import QtQml.Models 2.2 +import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 -Item { +Kirigami.ScrollablePage { id: root + title: i18n("Edit Course") - width: 400 //parent.width - height: 400 //parent.height - - Item { - id: theme - property string backgroundColor: "#ffffff" - property int smallIconSize: 18 - property int smallMediumIconSize: 22 - property int mediumIconSize: 32 - property int fontPointSize: 11 - } - - LanguageModel { - id: languageModel - view: LanguageModel.AllLanguages - resourceModel: LanguageResourceModel { - repository: g_repository + actions { + left: Kirigami.Action { + text: i18n("Previous") + tooltip: i18n("Switch to previous phrase.") + iconName: "go-previous" + enabled: g_editorSession.hasPreviousPhrase + onTriggered: g_editorSession.switchToPreviousPhrase() + } + right: Kirigami.Action { + text: i18n("Next") + tooltip: i18n("Switch to next phrase.") + iconName: "go-next" + enabled: g_editorSession.hasNextPhrase + onTriggered: g_editorSession.switchToNextPhrase() } - } - CourseModel { - id: courseModel - } - UnitModel { - id: selectedUnitModel - course: g_editorSession.displayedCourse } ColumnLayout { id: main anchors { fill: parent topMargin: 20 rightMargin: 20 bottomMargin: 20 leftMargin: 20 } spacing: 10 + LanguageModel { + id: languageModel + view: LanguageModel.AllLanguages + resourceModel: LanguageResourceModel { + repository: g_repository + } + } + CourseModel { + id: courseModel + } + UnitModel { + id: selectedUnitModel + course: g_editorSession.displayedCourse + } + RowLayout { Label { text: i18n("Course Prototype:") } ComboBox { Layout.minimumWidth: 300 model: SkeletonModel { id: skeletonModel } textRole: "title" onCurrentIndexChanged: { g_editorSession.skeleton = skeletonModel.skeleton(currentIndex) } } - Button { + Button { id: buttonEditSkeleton Layout.minimumWidth: 200 text: i18n("Edit Prototype") icon.name: "code-class" checkable: true enabled: g_editorSession.skeletonMode onClicked: g_editorSession.editSkeleton = checked } Item { Layout.fillWidth: true } Button { id: buttonSyncFromSkeleton enabled: !buttonEditSkeleton.checked Layout.minimumWidth: 200 text: i18n("Sync Prototype") icon.name: "view-refresh" ToolTip.visible: hovered ToolTip.delay: 1000 ToolTip.timeout: 5000 ToolTip.text: i18n("Update the course with elements from prototype.") onClicked: g_editorSession.updateCourseFromSkeleton() } } RowLayout { id: languageRow Label { text: i18n("Language:") } ComboBox { id: languageSelectionComboBox Layout.minimumWidth: 200 Layout.fillWidth: true enabled: !buttonEditSkeleton.checked model: languageModel textRole: "i18nTitle" onCurrentIndexChanged: { g_editorSession.setCourseByLanguage(languageModel.language(currentIndex)) } } } RowLayout { id: createNewCourseRow visible: { if (buttonEditSkeleton.checked || g_editorSession.displayedCourse !== null) { return false } return true } Label { text: i18n("There is no course in the selected language.") } ComboBox { // course selection only necessary when we do not edit skeleton derived course id: comboCourse visible: !g_editorSession.skeletonMode Layout.fillWidth: true model: CourseFilterModel { id: courseFilterModel courseModel: courseModel language: g_editorSession.language } textRole: "title" onCurrentIndexChanged: { if (courseFilterModel.course(currentIndex)) { g_editorSession.course = courseFilterModel.course(languageSelectionComboBox.currentIndex) } } onVisibleChanged: { if (visible && courseFilterModel.course(currentIndex)) { g_editorSession.course = courseFilterModel.course(languageSelectionComboBox.currentIndex) } } } Button { text: i18n("Create Course") icon.name: "journal-new" onClicked: { g_editorSession.course = g_repository.createCourse(languageModel.language(languageSelectionComboBox.currentIndex), g_editorSession.skeleton) } } - Item { Layout.fillHeight: true } //dummy + Button { // add units only if skeleton + id: newUnitButton + visible: !g_editorSession.skeletonMode || g_editorSession.editSkeleton + icon.name: "list-add" + text: i18n("New Unit") + onClicked: phraseModel.course.createUnit() + } } - RowLayout { - id: mainRow - visible: g_editorSession.course !== null && createNewCourseRow.visible === false - Layout.fillHeight: true - ColumnLayout { - ScrollView { - Layout.minimumWidth: Math.floor(main.width * 0.3) - Layout.fillHeight: true - QQC1.TreeView { - id: phraseTree - height: { - mainRow.height - - (newUnitButton.visible ? newUnitButton.height : 0) - - 10 - } - width: Math.floor(main.width * 0.3) - 20 - QQC1.TableViewColumn { - title: i18n("Units & Phrases") - role: "text" - } - model: PhraseModel { - id: phraseModel - course: g_editorSession.displayedCourse - } - selection: ItemSelectionModel { - model: phraseTree.model - } - itemDelegate: Item { - Text { - anchors.verticalCenter: parent.verticalCenter - color: styleData.textColor - elide: styleData.elideMode - text: styleData.value - } - } - onClicked: { - if (phraseModel.isPhrase(index)) { - g_editorSession.phrase = phraseModel.phrase(index) - } else { - g_editorSession.phrase = null - g_editorSession.unit = phraseModel.unit(index) - } - } - Connections { - target: g_editorSession - onPhraseChanged: { - if (g_editorSession.phrase === null) { - return - } - phraseTree.expand(phraseModel.indexUnit(g_editorSession.phrase.unit)) - phraseTree.selection.setCurrentIndex( - phraseModel.indexPhrase(g_editorSession.phrase), - ItemSelectionModel.ClearAndSelect) - } - } - } - } - Button { // add units only if skeleton - id: newUnitButton - visible: !g_editorSession.skeletonMode || g_editorSession.editSkeleton - icon.name: "list-add" - text: i18n("New Unit") - onClicked: phraseModel.course.createUnit() - } + + ColumnLayout { + UnitEditor { + visible: g_editorSession.unit !== null && g_editorSession.phrase === null + unit: g_editorSession.unit + editPhrases: g_editorSession.skeletonMode && g_editorSession.editSkeleton } - ColumnLayout { - UnitEditor { - visible: g_editorSession.unit !== null && g_editorSession.phrase === null - unit: g_editorSession.unit - editPhrases: g_editorSession.skeletonMode && g_editorSession.editSkeleton - } - PhraseEditor { - visible: g_editorSession.phrase !== null - phrase: g_editorSession.phrase - isSkeletonPhrase: g_editorSession.editSkeleton - Layout.minimumWidth: Math.floor(main.width * 0.6) - Layout.fillHeight: true - } + PhraseEditor { + visible: g_editorSession.phrase !== null + phrase: g_editorSession.phrase + isSkeletonPhrase: g_editorSession.editSkeleton + Layout.minimumWidth: Math.floor(main.width * 0.6) + Layout.fillHeight: true } } } } diff --git a/src/qml/Editor.qml b/src/qml/Editor.qml index 0e02ae4..291f91e 100644 --- a/src/qml/Editor.qml +++ b/src/qml/Editor.qml @@ -1,248 +1,66 @@ /* * Copyright 2013-2019 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.5 import QtQuick.Controls 2.3 import QtQuick.Controls 1.4 as QQC1 import QtQuick.Layouts 1.2 import QtQml.Models 2.2 +import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 -Item { +Kirigami.ApplicationWindow { id: root - width: 400 //parent.width - height: 400 //parent.height - - Item { - id: theme - property string backgroundColor: "#ffffff" - property int smallIconSize: 18 - property int smallMediumIconSize: 22 - property int mediumIconSize: 32 - property int fontPointSize: 11 + function changePage(pageItem) { + root.pageStack.clear(); + root.pageStack.push(pageItem); + root.pageStack.push(pageItem); } - LanguageModel { - id: languageModel - view: LanguageModel.AllLanguages - resourceModel: LanguageResourceModel { - repository: g_repository - } - } - CourseModel { - id: courseModel - } - UnitModel { - id: selectedUnitModel - course: g_editorSession.displayedCourse + globalDrawer: EditorDrawer { + pageStack: root.pageStack } - ColumnLayout { - id: main + pageStack.initialPage: welcomePageComponent + pageStack.globalToolBar.style: Kirigami.ApplicationHeaderStyle.Titles - anchors { - fill: parent - topMargin: 20 - rightMargin: 20 - bottomMargin: 20 - leftMargin: 20 - } - spacing: 10 - - RowLayout { - Label { - text: i18n("Course Prototype:") - } - ComboBox { - Layout.minimumWidth: 300 - model: SkeletonModel { - id: skeletonModel - } - textRole: "title" - onCurrentIndexChanged: { - g_editorSession.skeleton = skeletonModel.skeleton(currentIndex) - } - } - Button { - id: buttonEditSkeleton - Layout.minimumWidth: 200 - text: i18n("Edit Prototype") - icon.name: "code-class" - checkable: true - enabled: g_editorSession.skeletonMode - onClicked: g_editorSession.editSkeleton = checked - } - Item { Layout.fillWidth: true } - Button { - id: buttonSyncFromSkeleton - enabled: !buttonEditSkeleton.checked - Layout.minimumWidth: 200 - text: i18n("Sync Prototype") - icon.name: "view-refresh" - ToolTip.visible: hovered - ToolTip.delay: 1000 - ToolTip.timeout: 5000 - ToolTip.text: i18n("Update the course with elements from prototype.") - onClicked: g_editorSession.updateCourseFromSkeleton() - } - } - - RowLayout { - id: languageRow - Label { - text: i18n("Language:") - } - ComboBox { - id: languageSelectionComboBox - Layout.minimumWidth: 200 - Layout.fillWidth: true - enabled: !buttonEditSkeleton.checked - model: languageModel - textRole: "i18nTitle" - onCurrentIndexChanged: { - g_editorSession.setCourseByLanguage(languageModel.language(currentIndex)) - } - } - } - RowLayout { - id: createNewCourseRow - visible: { - if (buttonEditSkeleton.checked || g_editorSession.displayedCourse !== null) { - return false - } - return true - } - - Label { - text: i18n("There is no course in the selected language.") - } - ComboBox { // course selection only necessary when we do not edit skeleton derived course - id: comboCourse - visible: !g_editorSession.skeletonMode - Layout.fillWidth: true - model: CourseFilterModel { - id: courseFilterModel - courseModel: courseModel - language: g_editorSession.language - } - textRole: "title" - onCurrentIndexChanged: { - if (courseFilterModel.course(currentIndex)) { - g_editorSession.course = courseFilterModel.course(languageSelectionComboBox.currentIndex) - } - } - onVisibleChanged: { - if (visible && courseFilterModel.course(currentIndex)) { - g_editorSession.course = courseFilterModel.course(languageSelectionComboBox.currentIndex) - } - } - } - Button { - text: i18n("Create Course") - icon.name: "journal-new" - onClicked: { - g_editorSession.course = g_repository.createCourse(languageModel.language(languageSelectionComboBox.currentIndex), g_editorSession.skeleton) - } - } - Item { Layout.fillHeight: true } //dummy + // pages + Component { + id: welcomePageComponent + WelcomePageEditor { } + } + Component { + id: editCoursePageComponent + EditCoursePage { } + } + Component { + id: repositoryPageComponent + Item { + // TODO not implemented yet } - RowLayout { - id: mainRow - visible: g_editorSession.course !== null && createNewCourseRow.visible === false - Layout.fillHeight: true - ColumnLayout { - ScrollView { - Layout.minimumWidth: Math.floor(main.width * 0.3) - Layout.fillHeight: true - QQC1.TreeView { - id: phraseTree - height: { - mainRow.height - - (newUnitButton.visible ? newUnitButton.height : 0) - - 10 - } - width: Math.floor(main.width * 0.3) - 20 - QQC1.TableViewColumn { - title: i18n("Units & Phrases") - role: "text" - } - model: PhraseModel { - id: phraseModel - course: g_editorSession.displayedCourse - } - selection: ItemSelectionModel { - model: phraseTree.model - } - itemDelegate: Item { - Text { - anchors.verticalCenter: parent.verticalCenter - color: styleData.textColor - elide: styleData.elideMode - text: styleData.value - } - } - onClicked: { - if (phraseModel.isPhrase(index)) { - g_editorSession.phrase = phraseModel.phrase(index) - } else { - g_editorSession.phrase = null - g_editorSession.unit = phraseModel.unit(index) - } - } - Connections { - target: g_editorSession - onPhraseChanged: { - if (g_editorSession.phrase === null) { - return - } - phraseTree.expand(phraseModel.indexUnit(g_editorSession.phrase.unit)) - phraseTree.selection.setCurrentIndex( - phraseModel.indexPhrase(g_editorSession.phrase), - ItemSelectionModel.ClearAndSelect) - } - } - } - } - Button { // add units only if skeleton - id: newUnitButton - visible: !g_editorSession.skeletonMode || g_editorSession.editSkeleton - icon.name: "list-add" - text: i18n("New Unit") - onClicked: phraseModel.course.createUnit() - } - } - ColumnLayout { - UnitEditor { - visible: g_editorSession.unit !== null && g_editorSession.phrase === null - unit: g_editorSession.unit - editPhrases: g_editorSession.skeletonMode && g_editorSession.editSkeleton - } - PhraseEditor { - visible: g_editorSession.phrase !== null - phrase: g_editorSession.phrase - isSkeletonPhrase: g_editorSession.editSkeleton - Layout.minimumWidth: Math.floor(main.width * 0.6) - Layout.fillHeight: true - } - } + } + Component { + id: aboutPageComponent + Kirigami.AboutPage { + aboutData: g_artikulateAboutData } } } diff --git a/src/qml/ArtikulateDrawer.qml b/src/qml/EditorDrawer.qml similarity index 80% copy from src/qml/ArtikulateDrawer.qml copy to src/qml/EditorDrawer.qml index e55b5e9..9e2c9ab 100644 --- a/src/qml/ArtikulateDrawer.qml +++ b/src/qml/EditorDrawer.qml @@ -1,154 +1,147 @@ /* * Copyright 2018 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.5 import QtQuick.Layouts 1.3 import QtQuick.Controls 2.0 as QQC2 -import org.kde.kirigami 2.0 as Kirigami +import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 Kirigami.GlobalDrawer { id: root - title: "Artikulate" + title: "Editor" titleIcon: "artikulate" resetMenuOnTriggered: false bottomPadding: 0 property QtObject pageStack // enforce drawer always to be open modal: false handleVisible: false topContent: [ ColumnLayout { spacing: 0 Layout.fillWidth: true Layout.leftMargin: -root.leftPadding Layout.rightMargin: -root.rightPadding ActionListItem { action: Kirigami.Action { - text: i18n("Training") + text: i18n("Courses") iconName: "artikulate" onTriggered: { root.pageStack.clear(); root.pageStack.push(welcomePageComponent); } } } + ActionListItem { + action: Kirigami.Action { + text: i18n("Repository") + iconName: "document-edit" + onTriggered: { + root.pageStack.clear(); + root.pageStack.push(repositoryPageComponent); + } + } + } Kirigami.Separator { Layout.fillWidth: true } } ] // ordinary Kirigami actions are filled from training units/phrases actions: trainingActions.actions DrawerTrainingActions { id: trainingActions - session: g_trainingSession + session: g_editorSession onTriggerTrainingView: { root.pageStack.clear(); - root.pageStack.push(trainingPageComponent); + root.pageStack.push(editCoursePageComponent); } } //TODO integrate again // [ // Kirigami.Action { // text: i18n("Help") // iconName: "help-about" // Kirigami.Action { // text: i18n("Artikulate Handbook") // iconName: "help-contents" // onTriggered: { // triggerAction("help_contents"); // globalDrawer.resetMenu(); // } // } // Kirigami.Action { // text: i18n("Report Bug") // iconName: "tools-report-bug" // onTriggered: { // triggerAction("help_report_bug"); // globalDrawer.resetMenu(); // } // } // Kirigami.Action { // text: i18n("About KDE") // iconName: "help-about" // onTriggered: { // triggerAction("help_about_kde") // globalDrawer.resetMenu(); // } // } // } // ] ColumnLayout { spacing: 0 Layout.fillWidth: true Layout.leftMargin: -root.leftPadding Layout.rightMargin: -root.rightPadding Kirigami.Separator { Layout.fillWidth: true } -//TODO currently disabled while contents have to be ported +//TODO planned but not implemented // ActionListItem { // action: Kirigami.Action { -// text: i18n("Statistics") -// iconName: "user-properties" +// text: i18n("Upload Training") +// iconName: "get-hot-new-stuff" // onTriggered: { // root.pageStack.pop(); -// root.pageStack.push(profileSettingsPageComponent); +// root.pageStack.push(downloadPageComponent); // } // } // } -// ActionListItem { -// action: Kirigami.Action { -// text: i18n("Settings") -// iconName: "settings-configure" -// onTriggered: triggerSettingsDialog() -// } -// } - ActionListItem { - action: Kirigami.Action { - text: i18n("Download Training") - iconName: "get-hot-new-stuff" - onTriggered: { - root.pageStack.pop(); - root.pageStack.push(downloadPageComponent); - } - } - } ActionListItem { action: Kirigami.Action { text: i18n("About") iconName: "help-about" onTriggered: { root.pageStack.pop(); root.pageStack.push(aboutPageComponent); } } } } } diff --git a/src/qml/Main.qml b/src/qml/Main.qml index 2333782..061500d 100644 --- a/src/qml/Main.qml +++ b/src/qml/Main.qml @@ -1,83 +1,83 @@ /* * Copyright 2013-2017 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.5 import QtQuick.Controls 2.0 as QQC2 -import org.kde.kirigami 2.6 +import org.kde.kirigami 2.7 import artikulate 1.0 ApplicationWindow { id: root function changePage(pageItem) { root.pageStack.clear(); root.pageStack.push(pageItem); root.pageStack.push(pageItem); } globalDrawer: ArtikulateDrawer { pageStack: root.pageStack } contextDrawer: OverlayDrawer { id: contextDrawer } signal ghnsCourseDataStatusChanged(); signal triggerSettingsDialog(); signal triggerAction(string actionName); signal switchMenuBarVisibility(); property Learner learner: g_profileManager.activeProfile CourseModel { id: availableCourseModel } pageStack.initialPage: welcomePageComponent pageStack.globalToolBar.style: ApplicationHeaderStyle.Titles // pages Component { id: welcomePageComponent WelcomePage { } } Component { id: trainingPageComponent TrainingPage { } } Component { id: profileSettingsPageComponent ProfileSettingsPage { } } Component { id: aboutPageComponent AboutPage { aboutData: g_artikulateAboutData } } Component { id: downloadPageComponent DownloadPage { onStatusChanged: root.ghnsCourseDataStatusChanged() } } } diff --git a/src/qml/PhraseEditor.qml b/src/qml/PhraseEditor.qml index c13881d..1e6ae54 100644 --- a/src/qml/PhraseEditor.qml +++ b/src/qml/PhraseEditor.qml @@ -1,186 +1,165 @@ /* * Copyright 2013-2015 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.10 import QtQuick.Controls 2.3 import QtQuick.Layouts 1.2 import artikulate 1.0 Item { id: root property Phrase phrase property bool isSkeletonPhrase: false // use for saving property int __changedPhraseType property string __changedPhraseText width: 500 height: editLoader.height Component { id: editComponent Row { width: root.width height: { if (!root.isSkeletonPhrase) textEdit.height + phonemeGrid.height + phraseEditStateSetter.height + phraseRecorder.height + phraseTypeSetter.height; else { // height if only editing skeleton textEdit.height + phraseTypeSetter.height; } } ColumnLayout { id: textEdit height: inputLine.height + originalPhraseInfo.height width: parent.width spacing: 5 Row { id: originalPhraseInfo property string originalPhrase : (root.phrase != null) ? root.phrase.i18nText : "" spacing: 10 visible: { root.phrase != null && originalPhrase != "" && !root.isSkeletonPhrase} Text { text: i18n("Original Phrase: %1", originalPhraseInfo.originalPhrase) width: root.width - 70 wrapMode: Text.WordWrap } } RowLayout { // controls for setting phrase id: inputLine TextArea { id: phraseInput property Phrase phrase: root.phrase Layout.fillWidth: true Layout.maximumHeight: 100 text: root.phrase.text onTextChanged: { if (root.phrase == null) { return } root.phrase.text = text } onPhraseChanged: { if (root.phrase != null) text = root.phrase.text else text = "" } } } PhraseEditorTypeComponent { id: phraseTypeSetter phrase: root.phrase } PhraseEditorSoundComponent { id: phraseRecorder visible: !root.isSkeletonPhrase phrase: root.phrase } Component { id: phonemeItem Text { Button { width: 100 text: model.title checkable: true checked: { phrase != null && phrase.hasPhoneme(model.dataRole) } onClicked: { //TODO this button has no undo operation yet if (checked) { phrase.addPhoneme(model.dataRole) } else { phrase.removePhoneme(model.dataRole) } } } } } GridView { id: phonemeGrid property int columns : width / cellWidth width: root.width height: 30 * count / columns + 60 cellWidth: 100 cellHeight: 30 model: PhonemeModel { language: g_editorSession.language } delegate: phonemeItem } RowLayout { id: controls anchors { left: parent.left right: parent.right } PhraseEditorEditStateComponent { id: phraseEditStateSetter visible: !root.isSkeletonPhrase phrase: root.phrase } Label { // dummy Layout.fillWidth: true } - - ToolButton { - Layout.alignment: Qt.AlignBottom - width: 48 - height: 48 - enabled: g_editorSession.hasPreviousPhrase - icon.name: "go-previous" - onClicked: { - g_editorSession.switchToPreviousPhrase() - } - } - ToolButton { - Layout.alignment: Qt.AlignBottom - width: 48 - height: 48 - enabled: g_editorSession.hasNextPhrase - icon.name: "go-next" - onClicked: { - g_editorSession.switchToNextPhrase() - } - } } } } } ColumnLayout { id: phraseRow Loader { id: editLoader sourceComponent: (phrase != null) ? editComponent : undefined onSourceComponentChanged: { if (sourceComponent == undefined) height = 0 else height = editComponent.height } } } } diff --git a/src/qml/ProfileSettingsPage.qml b/src/qml/ProfileSettingsPage.qml index 561322d..ce28b54 100644 --- a/src/qml/ProfileSettingsPage.qml +++ b/src/qml/ProfileSettingsPage.qml @@ -1,133 +1,133 @@ /* * Copyright 2013-2018 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.1 import QtQuick.Controls 2.0 as QQC2 -import org.kde.kirigami 2.0 as Kirigami +import org.kde.kirigami 2.7 as Kirigami import QtQuick.Layouts 1.2 import artikulate 1.0 Kirigami.Page { id: root Layout.fillWidth: true title: i18n("Configure Profile") background: Rectangle { color: "#ffffff" } readonly property Learner profile: g_profileManager.activeProfile signal deletionRequest() actions { main: Kirigami.Action { id: editorAction text: i18n("Edit") iconName: "document-edit" checkable: true } } actions.contextualActions: [ Kirigami.Action { iconName: "insert-image" text: i18n("Select Image") onTriggered: { g_profileManager.openImageFileDialog() } }, Kirigami.Action { iconName: "edit-clear" text: i18n("Clear Image") onTriggered: { g_profileManager.activeProfile.clearImage() } }, Kirigami.Action { text: i18n("Delete User") iconName: "edit-delete" enabled: root.state == "editor" && g_profileManager.profileCount > 1 //TODO handle this situation gracefully by allowing removal of last profile onTriggered: deletionRequest() } ] // image MouseArea { id: image height: 120 width: height anchors { top: parent.top right: parent.right topMargin: 30 leftMargin: 30 } ProfileUserImageItem { anchors.centerIn: parent profile: root.profile } onClicked: g_profileManager.openImageFileDialog() } Text { id: learnerNameText anchors { horizontalCenter: parent.horizontalCenter top: image.bottom topMargin: 30 } height: paintedHeight font.pointSize: theme.fontPointSize * 1.2 text: root.profile != null ? root.profile.name : "" } QQC2.TextField { id: learnerNameTextEdit anchors { horizontalCenter: parent.horizontalCenter top: image.bottom topMargin: 30 } visible: false width: parent.width placeholderText: i18n("Name") text: learnerNameText.text onTextChanged: { root.profile.name = text g_profileManager.sync(root.profile) } } states: [ State { name: "editor" when: editorAction.checked PropertyChanges { target: learnerNameTextEdit visible: true } } ] } diff --git a/src/qml/WelcomePage.qml b/src/qml/WelcomePage.qml index d57db56..14f4ff8 100644 --- a/src/qml/WelcomePage.qml +++ b/src/qml/WelcomePage.qml @@ -1,85 +1,85 @@ /* * Copyright 2015-2019 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.1 import QtQuick.Controls 2.1 as QQC2 import QtQuick.Layouts 1.3 -import org.kde.kirigami 2.4 as Kirigami +import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 Kirigami.ScrollablePage { id: root title: i18n("Welcome to Artikulate") Kirigami.CardsListView { id: listView width: root.width - 40 model: CourseModel { id: courseModel } delegate: Kirigami.AbstractCard { contentItem: Item { implicitWidth: delegateLayout.implicitWidth implicitHeight: delegateLayout.implicitHeight GridLayout { id: delegateLayout anchors { left: parent.left top: parent.top right: parent.right } rowSpacing: Kirigami.Units.largeSpacing columnSpacing: Kirigami.Units.largeSpacing columns: width > Kirigami.Units.gridUnit * 20 ? 4 : 2 Kirigami.Icon { source: "language-artikulate" Layout.fillHeight: true Layout.maximumHeight: Kirigami.Units.iconSizes.huge Layout.preferredWidth: height } ColumnLayout { Kirigami.Heading { level: 2 text: i18nc("@title:window language / course name", "%1 / %2", model.language.title, model.title) } Kirigami.Separator { Layout.fillWidth: true } QQC2.Label { Layout.fillWidth: true wrapMode: Text.WordWrap text: model.description } } QQC2.Button { Layout.alignment: Qt.AlignRight|Qt.AlignVCenter Layout.columnSpan: 2 text: i18nc("@action:button", "Start Training") onClicked: { showPassiveNotification("Starting training session for course " + model.title + "."); g_trainingSession.course = model.dataRole } } } } } } } diff --git a/src/qml/WelcomePage.qml b/src/qml/WelcomePageEditor.qml similarity index 90% copy from src/qml/WelcomePage.qml copy to src/qml/WelcomePageEditor.qml index d57db56..eb3463d 100644 --- a/src/qml/WelcomePage.qml +++ b/src/qml/WelcomePageEditor.qml @@ -1,85 +1,85 @@ /* * Copyright 2015-2019 Andreas Cord-Landwehr * * 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 . */ import QtQuick 2.1 import QtQuick.Controls 2.1 as QQC2 import QtQuick.Layouts 1.3 -import org.kde.kirigami 2.4 as Kirigami +import org.kde.kirigami 2.7 as Kirigami import artikulate 1.0 Kirigami.ScrollablePage { id: root - title: i18n("Welcome to Artikulate") + title: i18n("Welcome to Artikulate Course Editor") Kirigami.CardsListView { id: listView width: root.width - 40 model: CourseModel { id: courseModel } delegate: Kirigami.AbstractCard { contentItem: Item { implicitWidth: delegateLayout.implicitWidth implicitHeight: delegateLayout.implicitHeight GridLayout { id: delegateLayout anchors { left: parent.left top: parent.top right: parent.right } rowSpacing: Kirigami.Units.largeSpacing columnSpacing: Kirigami.Units.largeSpacing columns: width > Kirigami.Units.gridUnit * 20 ? 4 : 2 Kirigami.Icon { source: "language-artikulate" Layout.fillHeight: true Layout.maximumHeight: Kirigami.Units.iconSizes.huge Layout.preferredWidth: height } ColumnLayout { Kirigami.Heading { level: 2 text: i18nc("@title:window language / course name", "%1 / %2", model.language.title, model.title) } Kirigami.Separator { Layout.fillWidth: true } QQC2.Label { Layout.fillWidth: true wrapMode: Text.WordWrap text: model.description } } QQC2.Button { Layout.alignment: Qt.AlignRight|Qt.AlignVCenter Layout.columnSpan: 2 - text: i18nc("@action:button", "Start Training") + text: i18nc("@action:button", "Select Course") onClicked: { - showPassiveNotification("Starting training session for course " + model.title + "."); - g_trainingSession.course = model.dataRole + showPassiveNotification("Selected course for editor: " + model.title + "."); + g_editorSession.course = model.dataRole } } } } } } } diff --git a/src/resources.qrc b/src/resources.qrc index 17cb202..8d93365 100644 --- a/src/resources.qrc +++ b/src/resources.qrc @@ -1,26 +1,29 @@ qml/ActionListItem.qml qml/ArtikulateDrawer.qml qml/DownloadPage.qml + qml/EditCoursePage.qml qml/Editor.qml + qml/EditorDrawer.qml qml/Main.qml qml/PhonemeUnitSelector.qml qml/PhraseEditor.qml qml/PhraseEditorEditStateComponent.qml qml/PhraseEditorSoundComponent.qml qml/PhraseEditorTypeComponent.qml qml/ProfileSelector.qml qml/ProfileSettingsPage.qml qml/ProfileUserImageItem.qml qml/SoundPlayer.qml qml/SoundRecorder.qml qml/TrainerCourseStatistics.qml qml/TrainingPage.qml qml/UnitEditor.qml qml/WelcomePage.qml + qml/WelcomePageEditor.qml artikulate.knsrc