Commit 9f51be34 authored by Bruno Coudoin's avatar Bruno Coudoin
Browse files

core, new internal dialog system instead of Qt Dialogs.

Qt Dialogs brings several issues for us:
- On desktop create external floating window
- On Android, brings the system toolbar that overrides our bar and
  never get away.
- Hard to port on some platforms
- Hard to translate YES / NO Button
- Cannot customize button with children aware icons

This implementation implemented our specific dialog system that are
easy to customize the way we need.
parent 9548861c
......@@ -24,7 +24,6 @@
import QtQuick 2.1
import GCompris 1.0
import QtGraphicalEffects 1.0
import QtQuick.Dialogs 1.1
import "../../core"
import "imageid.js" as Activity
......@@ -319,9 +318,9 @@ ActivityBase {
id: downloadWordsDialog
sourceComponent: GCDialog {
message: qsTr("The images for this activity are not yet installed.")
buttonText: qsTr("Download the images")
button1Text: qsTr("Download the images")
onClose: background.downloadWordsNeeded = false
onButtonHit: {
onButton1Hit: {
DownloadManager.resourceRegistered.connect(handleResourceRegistered);
DownloadManager.downloadResource(wordsResource)
var downloadDialog = Core.showDownloadDialog(activity, {});
......
......@@ -30,7 +30,7 @@ ActivityBase {
onHome: {
if(pageView.depth === 1) {
Core.quit(menuActivity);
Core.quit(main);
}
else {
pageView.pop();
......@@ -412,6 +412,7 @@ ActivityBase {
DialogConfig {
id: dialogConfig
main: menuActivity.main
onClose: {
ActivityInfoTree.filterByTag(menuActivity.currentTag)
ActivityInfoTree.filterLockedActivities()
......
......@@ -46,7 +46,7 @@ var clouds = new Array;
var cloudsErased = new Array;
function start(items_, dataset_) {
Core.checkForVoices(items_.bar);
Core.checkForVoices(items_.background);
items = items_
dataset = dataset_
......
......@@ -50,7 +50,7 @@ Item {
if (event.modifiers === Qt.ControlModifier &&
event.key === Qt.Key_Q) {
// Ctrl+Q exit the application
Core.quit(page);
Core.quit(main);
} else if (event.modifiers === Qt.ControlModifier &&
event.key === Qt.Key_B) {
// Ctrl+B toggle the bar
......
......@@ -219,7 +219,7 @@ Item {
BarButton {
source: "qrc:/gcompris/src/core/resource/bar_exit.svgz";
sourceSize.width: 66 * barZoom
onClicked: Core.quit(bar);
onClicked: Core.quit(bar.parent.parent);
}
}
Component {
......@@ -308,7 +308,7 @@ Item {
anchors.fill: parent
hoverEnabled: true
onClicked: {
var downloadDialog = Core.showDownloadDialog(bar, {});
var downloadDialog = Core.showDownloadDialog(bar.parent, {});
}
}
}
......
......@@ -113,7 +113,6 @@ if(BUILD_STANDALONE)
QtQuick/Window.2/${_lib_prefix}windowplugin
QtQuick/Particles.2/${_lib_prefix}particlesplugin
QtQuick/Layouts/${_lib_prefix}qquicklayoutsplugin
QtQuick/Dialogs/${_lib_prefix}dialogplugin
QtQuick.2/${_lib_prefix}qtquick2plugin
QtMultimedia/${_lib_prefix}declarative_multimedia)
......
......@@ -21,7 +21,6 @@
import QtQuick 2.2
import QtQuick.Controls 1.1
import QtQuick.Controls.Styles 1.1
import QtQuick.Dialogs 1.2
import GCompris 1.0
import QtQuick.Layouts 1.1
import "core.js" as Core
......@@ -32,6 +31,7 @@ Rectangle {
border.color: "black"
border.width: 1
z: 1000
property QtObject main
property bool isDialog: true
property string title
property string content
......@@ -581,21 +581,18 @@ Rectangle {
DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale)))
{
// ask for downloading new voices
var buttonHandler = new Array();
var dialog;
buttonHandler[StandardButton.No] = function() {};
buttonHandler[StandardButton.Yes] = function() {
// yes -> start download
if (DownloadManager.downloadResource(
DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale)))
var downloadDialog = Core.showDownloadDialog(main, {});
};
dialog = Core.showMessageDialog(dialogConfig,
qsTr("You selected a new locale"),
qsTr("Do you want to download the corresponding sound files now?"),
"",
StandardIcon.Question,
buttonHandler
console.log("main", main)
Core.showMessageDialog(main,
qsTr("You selected a new locale") + '\n'
+ qsTr("Do you want to download the corresponding sound files now?"),
"YES", function() {
// yes -> start download
if (DownloadManager.downloadResource(
DownloadManager.getVoicesResourceForLocale(ApplicationSettings.locale)))
var downloadDialog = Core.showDownloadDialog(main, {});
},
"NO", null,
null
);
} else // check for udpates or/and register new voices
DownloadManager.updateResource(
......
......@@ -22,17 +22,17 @@ import QtQuick 2.2
import GCompris 1.0
import QtQuick.Layouts 1.1
import QtQuick.Controls 1.2
import QtQuick.Dialogs 1.2
import "qrc:/gcompris/src/core/core.js" as Core
Dialog {
Item {
id: downloadDialog
opacity: 0
title: qsTr("Download in progress")
modality: Qt.ApplicationModal
standardButtons: StandardButton.NoButton
anchors {
fill: parent
}
property alias text: downloadDialogText.text
property Item main
property bool autohide: false; ///< whether to close the dialog automatically when download has finished
property bool reportSuccess: true; ///< whether to report successful
property bool reportError: true;
......@@ -41,8 +41,22 @@ Dialog {
property alias abortButtonVisible: abortButton.visible
property int fixedFontSize: 14 ///< fixed font-size used in this dialog
// start and stop trigs the animation
signal start
signal stop
// emitted at stop animation end
signal close
signal finished
onStart: opacity = 1
onStop: opacity = 0
onClose: destroy()
Behavior on opacity { NumberAnimation { duration: 200 } }
onOpacityChanged: opacity === 0 ? close() : null
function shutdown()
{
if (downloadDialog.dynamic)
......@@ -51,33 +65,135 @@ Dialog {
downloadDialog.close();
}
Rectangle {
anchors.fill: parent
opacity: 0.8
color: "grey"
MouseArea {
// Empty mouseArea to prevent clicking "behind" the Dialog
anchors.fill: parent
enabled: downloadDialog.opacity != 0
}
}
Item {
id: instruction
anchors {
horizontalCenter: parent.horizontalCenter
top: parent.top
topMargin: parent.height * 0.1
}
width: parent.width * 0.8
GCText {
id: instructionTxt
fontSize: mediumSize
color: "black"
horizontalAlignment: Text.AlignHCenter
width: parent.width
wrapMode: TextEdit.WordWrap
z: 2
height: 60 * ApplicationInfo.ratio
text: qsTr("Downloading ...")
}
Rectangle {
anchors.fill: instructionTxt
z: 1
opacity: 0.9
radius: 10
border.width: 2
border.color: "white"
gradient: Gradient {
GradientStop { position: 0.0; color: "#fff" }
GradientStop { position: 0.9; color: "#fff" }
GradientStop { position: 1.0; color: "#ddd" }
}
}
ProgressBar {
id: downloadDialogProgress
width: parent.width
anchors {
horizontalCenter: parent.horizontalCenter
top: instructionTxt.bottom
topMargin: 10
}
visible: true
Layout.alignment: Qt.AlignHCenter
Layout.rowSpan: 1
Layout.fillWidth: true
minimumValue: 0
maximumValue: 100
value: 0
}
Button {
id: backgroundButton
width: parent.width
height: 60 * ApplicationInfo.ratio
anchors {
horizontalCenter: parent.horizontalCenter
top: downloadDialogProgress.bottom
topMargin: 10
}
//: Run this task in background
text: qsTr("Background")
style: GCButtonStyle {
fixedFontSize: downloadDialog.fixedFontSize
}
visible: true
onClicked: downloadDialog.shutdown();
}
Button {
id: abortButton
width: parent.width
height: 60 * ApplicationInfo.ratio
anchors {
horizontalCenter: parent.horizontalCenter
top: backgroundButton.bottom
topMargin: 10
}
text: qsTr("Abort")
style: GCButtonStyle {
fixedFontSize: downloadDialog.fixedFontSize
}
visible: true
onClicked: {
if (DownloadManager.downloadIsRunning())
DownloadManager.abortDownloads();
downloadDialog.finished();
downloadDialog.shutdown();
}
}
}
Connections {
target: DownloadManager
onError: {
//console.warn("DownloadDialog: DM reports error: " + code + ": " + msg);
downloadDialog.finished();
if (downloadDialog.reportError
&& code != 5) { // no error: OperationCanceledError
&& code != 5) { // no error: OperationCanceledError
// show error message
downloadDialog.close();
var buttonHandler = new Array();
buttonHandler[StandardButton.Ignore] = function() {
downloadDialog.shutdown();
};
var messageDialog = Core.showMessageDialog(downloadDialog,
qsTr("Download error"),
code + ": " + msg,
"",
StandardIcon.Warning,
buttonHandler
);
} else
var messageDialog = Core.showMessageDialog(main,
qsTr("Download error") + code + ": " + msg,
"", null,
"", null,
function() {
downloadDialog.shutdown();
}
);
} else
downloadDialog.shutdown();
}
onDownloadProgress: downloadDialogProgress.value = 100 * bytesReceived / bytesTotal;
onDownloadStarted: {
//console.log("dialog: DM reports started: " + resource);
downloadDialogProgress.value = 0;
......@@ -87,89 +203,27 @@ Dialog {
//console.log("dialog: DM reports finished: " + code);
downloadDialog.finished();
if (downloadDialog.reportSuccess
&& code != 1) // note: errors will be reported by the onError handler
&& code != 1) // note: errors will be reported by the onError handler
{
// report success
downloadDialog.close();
var text;
downloadDialog.stop();
var infText = "";
if (code == 0) { // Success
text = qsTr("Your download finished successfully. The sound files are now available.");
infText = qsTr("Restart any currently active activity.");
infText = qsTr("Your download finished successfully. The sound files are now available.")
+ '\n'
+ qsTr("Restart any currently active activity.");
} else if (code == 2) // NoChange
text = qsTr("Your local sound files are up-to-date.")
var buttonHandler = new Array();
buttonHandler[StandardButton.Ok] = function() {
downloadDialog.shutdown();
};
var messageDialog = Core.showMessageDialog(downloadDialog,
qsTr("Download finished"),
text,
infText,
StandardIcon.Information,
buttonHandler
);
infText = qsTr("Your local sound files are up-to-date.")
var messageDialog = Core.showMessageDialog(main,
infText,
"", null,
"", null,
null
);
} else if (downloadDialog.autohide)
downloadDialog.shutdown();
}
}
ColumnLayout {
id: downloadColumn
width: parent.width
GCText {
id: downloadDialogText
width: parent.width
text: qsTr("Downloading ...")
textFormat: Text.StyledText
Layout.rowSpan: 1
Layout.fillWidth: true
fixFontSize: true
fontSize: fixedFontSize
wrapMode: Text.WordWrap
}
ProgressBar {
id: downloadDialogProgress
visible: true
Layout.alignment: Qt.AlignHCenter
Layout.rowSpan: 1
Layout.fillWidth: true
minimumValue: 0
maximumValue: 100
value: 0
}
RowLayout {
id: buttonRow
Layout.alignment: Qt.AlignHCenter
Layout.rowSpan: 1
Layout.fillWidth: true
Button {
id: backgroundButton
//: Run this task in background
text: qsTr("Background")
style: GCButtonStyle {
fixedFontSize: downloadDialog.fixedFontSize
}
visible: true
onClicked: downloadDialog.shutdown();
}
Button {
id: abortButton
width: downloadColumn.width/2
text: qsTr("Abort")
style: GCButtonStyle {
fixedFontSize: downloadDialog.fixedFontSize
}
visible: true
onClicked: {
if (DownloadManager.downloadIsRunning())
DownloadManager.abortDownloads();
downloadDialog.finished();
downloadDialog.shutdown();
}
}
}
}
}
......@@ -32,7 +32,8 @@ Item {
}
property alias message: instructionTxt.text
property alias buttonText: button.text
property alias button1Text: button1.text
property alias button2Text: button2.text
// start and stop trigs the animation
signal start
......@@ -41,17 +42,19 @@ Item {
// emitted at stop animation end
signal close
// emitted when the optional button is hit
signal buttonHit
signal button1Hit
signal button2Hit
onStart: opacity = 1
onStop: opacity = 0
onClose: destroy()
Behavior on opacity { NumberAnimation { duration: 200 } }
onOpacityChanged: opacity === 0 ? close() : null
Rectangle {
anchors.fill: parent
opacity: 0.6
opacity: 0.8
color: "grey"
}
......@@ -70,20 +73,12 @@ Item {
}
width: parent.width * 0.8
GCText {
id: instructionTxt
fontSize: mediumSize
color: "black"
horizontalAlignment: Text.AlignHCenter
width: parent.width
wrapMode: TextEdit.WordWrap
z: 2
onLinkActivated: Qt.openUrlExternally(link)
}
Rectangle {
anchors.fill: instructionTxt
id: instructionTxtBg
anchors.top: instruction.top
z: 1
width: parent.width
height: gcdialog.height - button1.height * 5
opacity: 0.9
radius: 10
border.width: 2
......@@ -93,22 +88,62 @@ Item {
GradientStop { position: 0.9; color: "#fff" }
GradientStop { position: 1.0; color: "#ddd" }
}
Flickable {
id: flick
anchors.margins: 8
anchors.fill: parent
contentWidth: instructionTxt.contentWidth
contentHeight: instructionTxt.contentHeight
flickableDirection: Flickable.VerticalFlick
clip: true
GCText {
id: instructionTxt
fontSize: regularSize
color: "black"
// @FIXME This property breaks the wrapping
// horizontalAlignment: Text.AlignHCenter
width: instruction.width
wrapMode: TextEdit.WordWrap
z: 2
onLinkActivated: Qt.openUrlExternally(link)
}
}
}
Button {
id: button1
width: parent.width
height: 60 * ApplicationInfo.ratio
anchors {
horizontalCenter: parent.horizontalCenter
top: instructionTxtBg.bottom
topMargin: 10
}
style: GCButtonStyle {
}
visible: text != ""
onClicked: {
gcdialog.button1Hit()
gcdialog.stop()
}
}
Button {
id: button
id: button2
width: parent.width
height: 60 * ApplicationInfo.ratio
anchors {
horizontalCenter: parent.horizontalCenter
top: instructionTxt.bottom
top: button1.bottom
topMargin: 10
}
style: GCButtonStyle {
}
visible: text != ""
onClicked: {
gcdialog.buttonHit()
gcdialog.button2Hit()
gcdialog.stop()
}
}
......
......@@ -26,7 +26,6 @@
*/
.pragma library
.import QtQuick.Dialogs 1.2 as Dialog
.import QtQml 2.2 as Qml
.import GCompris 1.0 as GCompris
......@@ -59,46 +58,6 @@ function getSoundFilenamForChar(c)
return result;
}
/* Helper funciton to workaround dialog-handler being triggered twice in
* Qt 5.3 (bug #35933) */
function callMeOnce(_dialog, _func)
{
var called = false;
var dialog = _dialog;
var func = _func;
return function() {
if (!called) {
func();
called = true;
destroyDialog(dialog);
}
}
}
function populateDialogButtons(dialog, buttonHandler)
{
for(var button in buttonHandler) {
// AcceptRole:
if (button == Dialog.StandardButton.Ok ||
button == Dialog.StandardButton.Ignore)
dialog.onAccepted.connect(callMeOnce(dialog, buttonHandler[button]));
// RejectRole:
else if (button == Dialog.StandardButton.Cancel ||
button == Dialog.StandardButton.Abort)
dialog.onRejected.connect(callMeOnce(dialog, buttonHandler[button]));
// YesRole:
else if (button == Dialog.StandardButton.Yes)
dialog.onYes.connect(callMeOnce(dialog, buttonHandler[button]));
// NoRole:
else if (button == Dialog.StandardButton.No)
dialog.onNo.connect(callMeOnce(dialog, buttonHandler[button]));
else {
throw new Error("Invalid standardButton: " + button);
}
dialog.standardButtons |= button;
}
}