openlp-core team mailing list archive
-
openlp-core team
-
Mailing list archive
-
Message #34678
[Merge] lp:~raoul-snyman/openlp/animated-alerts into lp:openlp
Raoul Snyman has proposed merging lp:~raoul-snyman/openlp/animated-alerts into lp:openlp.
Commit message:
Based on ic90's code, this just simplifies some of the code, and streamlines how things work, plus a couple of CSS fixes.
Requested reviews:
OpenLP Core (openlp-core)
For more details, see:
https://code.launchpad.net/~raoul-snyman/openlp/animated-alerts/+merge/372733
Based on ic90's code, this just simplifies some of the code, and streamlines how things work, plus a couple of CSS fixes.
--
Your team OpenLP Core is requested to review the proposed merge of lp:~raoul-snyman/openlp/animated-alerts into lp:openlp.
=== added file 'openlp/core/display/html/display.css'
--- openlp/core/display/html/display.css 1970-01-01 00:00:00 +0000
+++ openlp/core/display/html/display.css 2019-09-12 22:43:33 +0000
@@ -0,0 +1,90 @@
+@keyframes alert-scrolling-text {
+ 0% {
+ opacity: 1;
+ transform: translateX(100%);
+ }
+ 99% {
+ opacity: 1;
+ }
+ 100% {
+ opacity: 0;
+ transform: translateX(-101%);
+ }
+}
+
+body {
+ background: transparent !important;
+ color: rgb(255, 255, 255) !important;
+}
+
+sup {
+ vertical-align: super !important;
+ font-size: smaller !important;
+}
+
+.reveal .slides > section,
+.reveal .slides > section > section {
+ padding: 0;
+}
+
+.reveal > .backgrounds > .present {
+ visibility: hidden !important;
+}
+
+#global-background {
+ display: block;
+ visibility: visible;
+ z-index: -1;
+}
+
+.alert-container {
+ position: absolute;
+ display: flex;
+ flex-direction: row;
+ height: 100vh;
+ width: 100vw;
+}
+
+.hide {
+ opacity: 0 !important;
+ transition: opacity 0.5s ease;
+}
+
+.show {
+ opacity: 1 !important;
+ transition: opacity 0.5s ease;
+}
+
+.middle {
+ align-items: center;
+}
+
+.top {
+ align-items: flex-start;
+}
+
+.bottom {
+ align-items: flex-end;
+}
+
+#alert-background {
+ left: 0;
+ margin: 0;
+ opacity: 0;
+ overflow: hidden;
+ padding: 0.5em 0;
+ position: absolute;
+ transition: opacity 0.5s ease;
+ white-space: nowrap;
+ width: 100%;
+ z-index: 11;
+}
+
+#alert-text {
+ margin: 0 0.5em;
+ opacity: 0;
+ overflow: visible;
+ padding: 0;
+ transition: opacity 0.5s linear;
+ z-index: 100;
+}
=== modified file 'openlp/core/display/html/display.html'
--- openlp/core/display/html/display.html 2019-03-17 10:36:12 +0000
+++ openlp/core/display/html/display.html 2019-09-12 22:43:33 +0000
@@ -2,34 +2,16 @@
<html>
<head>
<title>Display Window</title>
- <link href="reveal.css" rel="stylesheet">
- <style type="text/css">
- body {
- background: transparent !important;
- color: rgb(255, 255, 255) !important;
- }
- sup {
- vertical-align: super !important;
- font-size: smaller !important;
- }
- .reveal .slides > section,
- .reveal .slides > section > section {
- padding: 0;
- }
- .reveal > .backgrounds > .present {
- visibility: hidden !important;
- }
- #global-background {
- display: block;
- visibility: visible;
- z-index: -1;
- }
- </style>
+ <link type="text/css" rel="stylesheet" href="reveal.css">
+ <link type="text/css" rel="stylesheet" href="display.css">
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script type="text/javascript" src="reveal.js"></script>
<script type="text/javascript" src="display.js"></script>
</head>
<body>
+ <div class="alert-container">
+ <div id="alert-background" class="hide"><div id="alert-text" class="hide">Testing alerts</div></div>
+ </div>
<div class="reveal">
<div id="global-background" class="slide-background present" data-loaded="true"></div>
<div class="slides"></div>
=== modified file 'openlp/core/display/html/display.js'
--- openlp/core/display/html/display.js 2019-08-22 16:40:45 +0000
+++ openlp/core/display/html/display.js 2019-09-12 22:43:33 +0000
@@ -53,6 +53,50 @@
};
/**
+ * Transition state enumeration
+ */
+var TransitionState = {
+ EntranceTransition: "entranceTransition",
+ NoTransition: "noTransition",
+ ExitTransition: "exitTransition"
+};
+
+/**
+ * Animation state enumeration
+ */
+var AnimationState = {
+ NoAnimation: "noAnimation",
+ ScrollingText: "scrollingText",
+ NonScrollingText: "noScrollingText"
+};
+
+/**
+ * Alert location enumeration
+ */
+var AlertLocation = {
+ Top: 0,
+ Middle: 1,
+ Bottom: 2
+};
+
+/**
+ * Alert state enumeration
+ */
+var AlertState = {
+ Displaying: "displaying",
+ NotDisplaying: "notDisplaying"
+}
+
+/**
+ * Alert delay enumeration
+ */
+var AlertDelay = {
+ FiftyMilliseconds: 50,
+ OneSecond: 1000,
+ OnePointFiveSeconds: 1500
+}
+
+/**
* Return an array of elements based on the selector query
* @param {string} selector - The selector to find elements
* @returns {array} An array of matching elements
@@ -118,6 +162,50 @@
}
/**
+ * Change a camelCaseString to a camel-case-string
+ * @private
+ * @param {string} text
+ * @returns {string} the Un-camel-case-ified string
+ */
+function _fromCamelCase(text) {
+ return text.replace(/([A-Z])/g, function (match, submatch) {
+ return '-' + submatch.toLowerCase();
+ });
+}
+
+/**
+ * Create a CSS style
+ * @private
+ * @param {string} selector - The selector for this style
+ * @param {Object} rules - The rules to apply to the style
+ */
+function _createStyle(selector, rules) {
+ var id = selector.replace("#", "").replace(" .", "-").replace(".", "-").replace(" ", "_");
+ if ($("style#" + id).length != 0) {
+ var style = $("style#" + id)[0];
+ }
+ else {
+ var style = document.createElement("style");
+ document.getElementsByTagName("head")[0].appendChild(style);
+ style.type = "text/css";
+ style.id = id;
+ }
+ var rulesString = selector + " { ";
+ for (var key in rules) {
+ var ruleValue = rules[key];
+ var ruleKey = _fromCamelCase(key);
+ rulesString += "" + ruleKey + ": " + ruleValue + ";";
+ }
+ rulesString += " } ";
+ if (style.styleSheet) {
+ style.styleSheet.cssText = rulesString;
+ }
+ else {
+ style.appendChild(document.createTextNode(rulesString));
+ }
+}
+
+/**
* An audio player with a play list
*/
var AudioPlayer = function (audioElement) {
@@ -234,7 +322,12 @@
* The Display object is what we use from OpenLP
*/
var Display = {
+ _alerts: [],
_slides: {},
+ _alertSettings: {},
+ _alertState: AlertState.NotDisplaying,
+ _transitionState: TransitionState.NoTransition,
+ _animationState: AnimationState.NoAnimation,
_revealConfig: {
margin: 0.0,
minScale: 1.0,
@@ -355,21 +448,149 @@
Display.reinit();
},
/**
- * Display an alert
- * @param {string} text - The alert text
- * @param {int} location - The location of the text (top, middle or bottom)
- */
- alert: function (text, location) {
- console.debug(" alert text: " + text, ", location: " + location);
- /*
- * The implementation should show an alert.
- * It should be able to handle receiving a new alert before a previous one is "finished", basically queueing it.
- */
- return;
-},
-
- /**
- * Add a slides. If the slide exists but the HTML is different, update the slide.
+ * Display an alert. If there's an alert already showing, add this one to the queue
+ * @param {string} text - The alert text
+ * @param {Object} JSON object - The settings for the alert object
+ */
+ alert: function (text, settings) {
+ if (text == "") {
+ return null;
+ }
+ if (Display._alertState === AlertState.Displaying) {
+ console.debug("Adding to queue");
+ Display.addAlertToQueue(text, settings);
+ }
+ else {
+ console.debug("Displaying immediately");
+ Display.showAlert(text, settings);
+ }
+ },
+ /**
+ * Show the alert on the screen
+ * @param {string} text - The alert text
+ * @param {Object} JSON object - The settings for the alert
+ */
+ showAlert: function (text, settings) {
+ var alertBackground = $('#alert-background')[0];
+ var alertText = $('#alert-text')[0];
+ // create styles for the alerts from the settings
+ _createStyle("#alert-background.settings", {
+ backgroundColor: settings["backgroundColor"],
+ fontFamily: "'" + settings["fontFace"] + "'",
+ fontSize: settings["fontSize"].toString() + "pt",
+ color: settings["fontColor"]
+ });
+ alertBackground.classList.add("settings");
+ alertBackground.classList.replace("hide", "show");
+ alertText.innerHTML = text;
+ Display.setAlertLocation(settings.location);
+ Display._transitionState = TransitionState.EntranceTransition;
+ /* Check if the alert is a queued alert */
+ if (Display._alertState !== AlertState.Displaying) {
+ Display._alertState = AlertState.Displaying;
+ }
+ alertBackground.addEventListener('transitionend', Display.alertTransitionEndEvent, false);
+ alertText.addEventListener('animationend', Display.alertAnimationEndEvent, false);
+ /* Either scroll the alert, or make it disappear at the end of its time */
+ if (settings.scroll) {
+ Display._animationState = AnimationState.ScrollingText;
+ var animationSettings = "alert-scrolling-text " + settings.timeout +
+ "s linear 0.6s " + settings.repeat + " normal";
+ alertText.style.animation = animationSettings;
+ }
+ else {
+ Display._animationState = AnimationState.NonScrollingText;
+ alertText.classList.replace("hide", "show");
+ setTimeout (function () {
+ Display._animationState = AnimationState.NoAnimation;
+ Display.hideAlert();
+ }, settings.timeout * AlertDelay.OneSecond);
+ }
+ },
+ /**
+ * Hide the alert at the end
+ */
+ hideAlert: function () {
+ var alertBackground = $('#alert-background')[0];
+ var alertText = $('#alert-text')[0];
+ Display._transitionState = TransitionState.ExitTransition;
+ alertText.classList.replace("show", "hide");
+ alertBackground.classList.replace("show", "hide");
+ alertText.style.animation = "";
+ Display._alertState = AlertState.NotDisplaying;
+ },
+ /**
+ * Add an alert to the alert queue
+ * @param {string} text - The alert text to be displayed
+ * @param {Object} setttings - JSON object containing the settings for the alert
+ */
+ addAlertToQueue: function (text, settings) {
+ Display._alerts.push({text: text, settings: settings});
+ },
+ /**
+ * The alertTransitionEndEvent called after a transition has ended
+ */
+ alertTransitionEndEvent: function (e) {
+ e.stopPropagation();
+ console.debug("Transition end event reached: " + Display._transitionState);
+ if (Display._transitionState === TransitionState.EntranceTransition) {
+ Display._transitionState = TransitionState.NoTransition;
+ }
+ else if (Display._transitionState === TransitionState.ExitTransition) {
+ Display._transitionState = TransitionState.NoTransition;
+ Display.hideAlert();
+ Display.showNextAlert();
+ }
+ },
+ /**
+ * The alertAnimationEndEvent called after an animation has ended
+ */
+ alertAnimationEndEvent: function (e) {
+ e.stopPropagation();
+ Display.hideAlert();
+ },
+ /**
+ * Set the location of the alert
+ * @param {int} location - Integer number with the location of the alert on screen
+ */
+ setAlertLocation: function (location) {
+ var alertContainer = $(".alert-container")[0];
+ // Remove an existing location classes
+ alertContainer.classList.remove("top");
+ alertContainer.classList.remove("middle");
+ alertContainer.classList.remove("bottom");
+ // Apply the location class we want
+ switch (location) {
+ case AlertLocation.Top:
+ alertContainer.classList.add("top");
+ break;
+ case AlertLocation.Middle:
+ alertContainer.classList.add("middle");
+ break;
+ case AlertLocation.Bottom:
+ default:
+ alertContainer.classList.add("bottom");
+ break;
+ }
+ },
+ /**
+ * Display the next alert in the queue
+ */
+ showNextAlert: function () {
+ console.log("showNextAlert");
+ if (Display._alerts.length > 0) {
+ console.log("Showing next alert");
+ var alertObject = Display._alerts.shift();
+ Display._alertState = AlertState.DisplayingFromQueue;
+ Display.showAlert(alertObject.text, alertObject.settings);
+ }
+ else {
+ // For the tests
+ return null;
+ }
+ },
+ /**
+ * Add a slide. If the slide exists but the HTML is different, update the slide.
* @param {string} verse - The verse number, e.g. "v1"
* @param {string} text - The HTML for the verse, e.g. "line1<br>line2"
* @param {string} footer_text - The HTML for the footer"
@@ -770,7 +991,7 @@
return videoTypes;
},
/**
- * Sets the scale of the page - used to make preview widgets scale
+ * Sets the scale of the page - used to make preview widgets scale
*/
setScale: function(scale) {
document.body.style.zoom = scale+"%";
=== modified file 'openlp/core/display/webengine.py'
--- openlp/core/display/webengine.py 2019-04-13 13:00:22 +0000
+++ openlp/core/display/webengine.py 2019-09-12 22:43:33 +0000
@@ -27,6 +27,8 @@
from PyQt5 import QtCore, QtWebEngineWidgets, QtWidgets
+from openlp.core.common.applocation import AppLocation
+
LOG_LEVELS = {
QtWebEngineWidgets.QWebEnginePage.InfoMessageLevel: logging.INFO,
@@ -46,6 +48,10 @@
"""
Override the parent method in order to log the messages in OpenLP
"""
+ # The JS log has the entire file location, which we don't really care about
+ app_dir = AppLocation.get_directory(AppLocation.AppDir).parent
+ source_id = source_id.replace('file://{app_dir}/'.format(app_dir=app_dir), '')
+ # Log the JS messages to the Python logger
log.log(LOG_LEVELS[level], '{source_id}:{line_number} {message}'.format(source_id=source_id,
line_number=line_number,
message=message))
=== modified file 'openlp/core/display/window.py'
--- openlp/core/display/window.py 2019-09-10 21:37:12 +0000
+++ openlp/core/display/window.py 2019-09-12 22:43:33 +0000
@@ -399,8 +399,8 @@
self.scale = scale
self.run_javascript('Display.setScale({scale});'.format(scale=scale * 100))
- def alert(self, text, location):
+ def alert(self, text, settings):
"""
Set an alert
"""
- self.run_javascript('Display.alert({text}, {location});'.format(text=text, location=location))
+ self.run_javascript('Display.alert("{text}", {settings});'.format(text=text, settings=settings))
=== modified file 'openlp/plugins/alerts/alertsplugin.py'
--- openlp/plugins/alerts/alertsplugin.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/alerts/alertsplugin.py 2019-09-12 22:43:33 +0000
@@ -126,7 +126,9 @@
'alerts/location': AlertLocation.Bottom,
'alerts/background color': '#660000',
'alerts/font color': '#ffffff',
- 'alerts/timeout': 5
+ 'alerts/timeout': 10,
+ 'alerts/repeat': 1,
+ 'alerts/scroll': True
}
=== modified file 'openlp/plugins/alerts/lib/alertsmanager.py'
--- openlp/plugins/alerts/lib/alertsmanager.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/alerts/lib/alertsmanager.py 2019-09-12 22:43:33 +0000
@@ -23,7 +23,9 @@
The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of the plugin which manages storing and
displaying of alerts.
"""
-from PyQt5 import QtCore
+import json
+
+from PyQt5 import QtCore, QtGui
from openlp.core.common.i18n import translate
from openlp.core.common.mixins import LogMixin, RegistryProperties
@@ -83,8 +85,26 @@
not Settings().value('core/display on monitor')):
return
text = self.alert_list.pop(0)
- alert_tab = self.parent().settings_tab
- self.live_controller.displays[0].alert(text, alert_tab.location)
+
+ # Get the rgb color format of the font & background hex colors from settings
+ rgb_font_color = self.hex_to_rgb(QtGui.QColor(Settings().value('alerts/font color')))
+ rgb_background_color = self.hex_to_rgb(QtGui.QColor(Settings().value('alerts/background color')))
+
+ # Put alert settings together in dict that will be passed to Display in Javascript
+ alert_settings = {
+ 'backgroundColor': rgb_background_color,
+ 'location': Settings().value('alerts/location'),
+ 'fontFace': Settings().value('alerts/font face'),
+ 'fontSize': Settings().value('alerts/font size'),
+ 'fontColor': rgb_font_color,
+ 'timeout': Settings().value('alerts/timeout'),
+ 'repeat': Settings().value('alerts/repeat'),
+ 'scroll': Settings().value('alerts/scroll')
+ }
+ self.live_controller.displays[0].alert(text, json.dumps(alert_settings))
+ # Check to see if we have a timer running.
+ # if self.timer_id == 0:
+ # self.timer_id = self.startTimer(int(alert_tab.timeout) * 1000)
def timerEvent(self, event):
"""
@@ -98,3 +118,13 @@
self.killTimer(self.timer_id)
self.timer_id = 0
self.generate_alert()
+
+ def hex_to_rgb(self, rgb_values):
+ """
+ Converts rgb color values from QColor to rgb string
+
+ :param rgb_values:
+ :return: rgb color string
+ :rtype: string
+ """
+ return "rgb(" + str(rgb_values.red()) + ", " + str(rgb_values.green()) + ", " + str(rgb_values.blue()) + ")"
=== modified file 'openlp/plugins/alerts/lib/alertstab.py'
--- openlp/plugins/alerts/lib/alertstab.py 2019-04-13 13:00:22 +0000
+++ openlp/plugins/alerts/lib/alertstab.py 2019-09-12 22:43:33 +0000
@@ -47,35 +47,56 @@
self.font_layout.addRow(self.font_label, self.font_combo_box)
self.font_color_label = QtWidgets.QLabel(self.font_group_box)
self.font_color_label.setObjectName('font_color_label')
- self.color_layout = QtWidgets.QHBoxLayout()
- self.color_layout.setObjectName('color_layout')
self.font_color_button = ColorButton(self.font_group_box)
self.font_color_button.setObjectName('font_color_button')
- self.color_layout.addWidget(self.font_color_button)
- self.color_layout.addSpacing(20)
- self.background_color_label = QtWidgets.QLabel(self.font_group_box)
- self.background_color_label.setObjectName('background_color_label')
- self.color_layout.addWidget(self.background_color_label)
- self.background_color_button = ColorButton(self.font_group_box)
- self.background_color_button.setObjectName('background_color_button')
- self.color_layout.addWidget(self.background_color_button)
- self.font_layout.addRow(self.font_color_label, self.color_layout)
+ self.font_layout.addRow(self.font_color_label, self.font_color_button)
self.font_size_label = QtWidgets.QLabel(self.font_group_box)
self.font_size_label.setObjectName('font_size_label')
self.font_size_spin_box = QtWidgets.QSpinBox(self.font_group_box)
self.font_size_spin_box.setObjectName('font_size_spin_box')
self.font_layout.addRow(self.font_size_label, self.font_size_spin_box)
- self.timeout_label = QtWidgets.QLabel(self.font_group_box)
+ self.left_layout.addWidget(self.font_group_box)
+ # Background Settings
+ self.background_group_box = QtWidgets.QGroupBox(self.left_column)
+ self.background_group_box.setObjectName('background_group_box')
+ self.background_layout = QtWidgets.QFormLayout(self.background_group_box)
+ self.background_layout.setObjectName('background_settings_layout')
+ self.background_color_label = QtWidgets.QLabel(self.background_group_box)
+ self.background_color_label.setObjectName('background_color_label')
+ self.background_color_button = ColorButton(self.background_group_box)
+ self.background_color_button.setObjectName('background_color_button')
+ self.background_layout.addRow(self.background_color_label,self.background_color_button)
+ self.left_layout.addWidget(self.background_group_box)
+ # Scroll Settings
+ self.scroll_group_box = QtWidgets.QGroupBox(self.left_column)
+ self.scroll_group_box.setObjectName('scroll_group_box')
+ self.scroll_group_layout = QtWidgets.QFormLayout(self.scroll_group_box)
+ self.scroll_group_layout.setObjectName('scroll_group_layout')
+ self.scroll_check_box = QtWidgets.QCheckBox(self.scroll_group_box)
+ self.scroll_check_box.setObjectName('scroll_check_box')
+ self.scroll_group_layout.addRow(self.scroll_check_box)
+ self.repeat_label = QtWidgets.QLabel(self.scroll_group_box)
+ self.repeat_label.setObjectName('repeat_label')
+ self.repeat_spin_box = QtWidgets.QSpinBox(self.scroll_group_box)
+ self.repeat_spin_box.setObjectName('repeat_spin_box')
+ self.scroll_group_layout.addRow(self.repeat_label, self.repeat_spin_box)
+ self.left_layout.addWidget(self.scroll_group_box)
+ # Other Settings
+ self.settings_group_box = QtWidgets.QGroupBox(self.left_column)
+ self.settings_group_box.setObjectName('settings_group_box')
+ self.settings_layout = QtWidgets.QFormLayout(self.settings_group_box)
+ self.settings_layout.setObjectName('settings_layout')
+ self.timeout_label = QtWidgets.QLabel(self.settings_group_box)
self.timeout_label.setObjectName('timeout_label')
- self.timeout_spin_box = QtWidgets.QSpinBox(self.font_group_box)
+ self.timeout_spin_box = QtWidgets.QSpinBox(self.settings_group_box)
self.timeout_spin_box.setMaximum(180)
self.timeout_spin_box.setObjectName('timeout_spin_box')
- self.font_layout.addRow(self.timeout_label, self.timeout_spin_box)
+ self.settings_layout.addRow(self.timeout_label, self.timeout_spin_box)
self.vertical_label, self.vertical_combo_box = create_valign_selection_widgets(self.font_group_box)
self.vertical_label.setObjectName('vertical_label')
self.vertical_combo_box.setObjectName('vertical_combo_box')
- self.font_layout.addRow(self.vertical_label, self.vertical_combo_box)
- self.left_layout.addWidget(self.font_group_box)
+ self.settings_layout.addRow(self.vertical_label, self.vertical_combo_box)
+ self.left_layout.addWidget(self.settings_group_box)
self.left_layout.addStretch()
self.preview_group_box = QtWidgets.QGroupBox(self.right_column)
self.preview_group_box.setObjectName('preview_group_box')
@@ -92,16 +113,22 @@
self.font_combo_box.activated.connect(self.on_font_combo_box_clicked)
self.timeout_spin_box.valueChanged.connect(self.on_timeout_spin_box_changed)
self.font_size_spin_box.valueChanged.connect(self.on_font_size_spin_box_changed)
+ self.repeat_spin_box.valueChanged.connect(self.on_repeat_spin_box_changed)
+ self.scroll_check_box.toggled.connect(self.scroll_check_box_toggled)
def retranslate_ui(self):
- self.font_group_box.setTitle(translate('AlertsPlugin.AlertsTab', 'Font'))
+ self.font_group_box.setTitle(translate('AlertsPlugin.AlertsTab', 'Font Settings'))
self.font_label.setText(translate('AlertsPlugin.AlertsTab', 'Font name:'))
self.font_color_label.setText(translate('AlertsPlugin.AlertsTab', 'Font color:'))
self.background_color_label.setText(UiStrings().BackgroundColorColon)
self.font_size_label.setText(translate('AlertsPlugin.AlertsTab', 'Font size:'))
self.font_size_spin_box.setSuffix(' {unit}'.format(unit=UiStrings().FontSizePtUnit))
+ self.background_group_box.setTitle(translate('AlertsPlugin.AlertsTab', 'Background Settings'))
+ self.settings_group_box.setTitle(translate('AlertsPlugin.AlertsTab', 'Other Settings'))
self.timeout_label.setText(translate('AlertsPlugin.AlertsTab', 'Alert timeout:'))
self.timeout_spin_box.setSuffix(' {unit}'.format(unit=UiStrings().Seconds))
+ self.repeat_label.setText(translate('AlertsPlugin.AlertsTab', 'Repeat (no. of times):'))
+ self.scroll_check_box.setText(translate('AlertsPlugin.AlertsTab', 'Enable Scrolling'))
self.preview_group_box.setTitle(UiStrings().Preview)
self.font_preview.setText(UiStrings().OpenLP)
@@ -140,6 +167,24 @@
self.font_size = self.font_size_spin_box.value()
self.update_display()
+ def on_repeat_spin_box_changed(self):
+ """
+ The repeat spin box has changed
+ """
+ self.repeat = self.repeat_spin_box.value()
+ self.changed = True
+
+ def scroll_check_box_toggled(self):
+ """
+ The scrolling checkbox has been toggled
+ """
+ if self.scroll_check_box.isChecked():
+ self.repeat_spin_box.setEnabled(True)
+ else:
+ self.repeat_spin_box.setEnabled(False)
+ self.scroll = self.scroll_check_box.isChecked()
+ self.changed = True
+
def load(self):
"""
Load the settings into the UI.
@@ -152,12 +197,17 @@
self.background_color = settings.value('background color')
self.font_face = settings.value('font face')
self.location = settings.value('location')
+ self.repeat = settings.value('repeat')
+ self.scroll = settings.value('scroll')
settings.endGroup()
self.font_size_spin_box.setValue(self.font_size)
self.timeout_spin_box.setValue(self.timeout)
self.font_color_button.color = self.font_color
self.background_color_button.color = self.background_color
+ self.repeat_spin_box.setValue(self.repeat)
+ self.repeat_spin_box.setEnabled(self.scroll)
self.vertical_combo_box.setCurrentIndex(self.location)
+ self.scroll_check_box.setChecked(self.scroll)
font = QtGui.QFont()
font.setFamily(self.font_face)
self.font_combo_box.setCurrentFont(font)
@@ -181,6 +231,8 @@
settings.setValue('timeout', self.timeout)
self.location = self.vertical_combo_box.currentIndex()
settings.setValue('location', self.location)
+ settings.setValue('repeat', self.repeat)
+ settings.setValue('scroll', self.scroll_check_box.isChecked())
settings.endGroup()
if self.changed:
self.settings_form.register_post_process('update_display_css')
=== modified file 'tests/js/test_display.js'
--- tests/js/test_display.js 2019-08-22 16:40:45 +0000
+++ tests/js/test_display.js 2019-09-12 22:43:33 +0000
@@ -27,6 +27,14 @@
it("AudioState should exist", function () {
expect(AudioState).toBeDefined();
});
+
+ it("TransitionState should exist", function(){
+ expect(TransitionState).toBeDefined();
+ });
+
+ it("AnimationState should exist", function(){
+ expect(AnimationState).toBeDefined();
+ });
});
describe("The function", function () {
@@ -141,6 +149,296 @@
Display.goToSlide("v1");
expect(Reveal.slide).toHaveBeenCalledWith(0);
});
+
+ it("should have an alert() method", function () {
+ expect(Display.alert).toBeDefined();
+ });
+
+});
+
+describe("Display.alert", function () {
+ var alertContainer, alertBackground, alertText, settings, text;
+
+ beforeEach(function () {
+ document.body.innerHTML = "";
+ alertContainer = _createDiv({"class": "alert-container"});
+ alertBackground = _createDiv({"id": "alert-background", "class": "hide"});
+ alertText = _createDiv({"id": "alert-text", "class": "hide"});
+ settings = {
+ "location": 1,
+ "fontFace": "sans-serif",
+ "fontSize": 40,
+ "fontColor": "#ffffff",
+ "backgroundColor": "#660000",
+ "timeout": 5,
+ "repeat": 1,
+ "scroll": true
+ };
+ text = "Display.alert";
+ });
+
+ it("should return null if called without any text", function () {
+ expect(Display.alert("", settings)).toBeNull();
+ });
+
+ it("should set the correct alert text", function () {
+ spyOn(Display, "showAlert");
+
+ Display.alert(text, settings);
+
+ expect(Display.showAlert).toHaveBeenCalled();
+ });
+
+ it("should call the addAlertToQueue method if an alert is displaying", function () {
+ spyOn(Display, "addAlertToQueue");
+ Display._alerts = [];
+ Display._alertState = AlertState.Displaying;
+
+ Display.alert(text, settings);
+
+ expect(Display.addAlertToQueue).toHaveBeenCalledWith(text, settings);
+ });
+});
+
+describe("Display.showAlert", function () {
+ var alertContainer, alertBackground, alertText, settings;
+
+ beforeEach(function () {
+ document.body.innerHTML = "";
+ alertContainer = _createDiv({"class": "alert-container"});
+ alertBackground = _createDiv({"id": "alert-background", "class": "hide"});
+ alertText = _createDiv({"id": "alert-text", "class": "hide"});
+ settings = {
+ "location": 1,
+ "fontFace": "sans-serif",
+ "fontSize": 40,
+ "fontColor": "#ffffff",
+ "backgroundColor": "#660000",
+ "timeout": 5,
+ "repeat": 1,
+ "scroll": true
+ };
+ });
+
+ it("should create a stylesheet for the settings", function () {
+ spyOn(window, "_createStyle");
+ Display.showAlert("Test Display.showAlert - stylesheet", settings);
+
+ expect(_createStyle).toHaveBeenCalledWith("#alert-background.settings", {
+ backgroundColor: settings["backgroundColor"],
+ fontFamily: "'" + settings["fontFace"] + "'",
+ fontSize: settings["fontSize"] + 'pt',
+ color: settings["fontColor"]
+ });
+ });
+
+ it("should set the alert state to AlertState.Displaying", function () {
+ Display.showAlert("Test Display.showAlert - state", settings);
+
+ expect(Display._alertState).toEqual(AlertState.Displaying);
+ });
+
+ it("should remove the 'hide' classes and add the 'show' classes", function () {
+ Display.showAlert("Test Display.showAlert - classes", settings);
+
+ expect($("#alert-background")[0].classList.contains("hide")).toEqual(false);
+ expect($("#alert-background")[0].classList.contains("show")).toEqual(true);
+ //expect($("#alert-text")[0].classList.contains("hide")).toEqual(false);
+ //expect($("#alert-text")[0].classList.contains("show")).toEqual(true);
+ });
+});
+
+describe("Display.hideAlert", function () {
+ var alertContainer, alertBackground, alertText, settings;
+
+ beforeEach(function () {
+ document.body.innerHTML = "";
+ alertContainer = _createDiv({"class": "alert-container"});
+ alertBackground = _createDiv({"id": "alert-background", "class": "hide"});
+ alertText = _createDiv({"id": "alert-text", "class": "hide"});
+ settings = {
+ "location": 1,
+ "fontFace": "sans-serif",
+ "fontSize": 40,
+ "fontColor": "#ffffff",
+ "backgroundColor": "#660000",
+ "timeout": 5,
+ "repeat": 1,
+ "scroll": true
+ };
+ });
+
+ it("should set the alert state to AlertState.NotDisplaying", function () {
+ Display.showAlert("test", settings);
+
+ Display.hideAlert();
+
+ expect(Display._alertState).toEqual(AlertState.NotDisplaying);
+ });
+
+ it("should hide the alert divs when called", function() {
+ Display.showAlert("test", settings);
+
+ Display.hideAlert();
+
+ expect(Display._transitionState).toEqual(TransitionState.ExitTransition);
+ expect(alertBackground.classList.contains("hide")).toEqual(true);
+ expect(alertBackground.classList.contains("show")).toEqual(false);
+ expect(alertText.classList.contains("hide")).toEqual(true);
+ expect(alertText.classList.contains("show")).toEqual(false);
+ });
+});
+
+describe("Display.setAlertLocation", function() {
+ var alertContainer, alertBackground, alertText, settings;
+
+ beforeEach(function () {
+ document.body.innerHTML = "";
+ alertContainer = _createDiv({"class": "alert-container"});
+ alertBackground = _createDiv({"id": "alert-background", "class": "hide"});
+ alertText = _createDiv({"id": "alert-text", "class": "hide"});
+ settings = {
+ "location": 1,
+ "fontFace": "sans-serif",
+ "fontSize": 40,
+ "fontColor": "#ffffff",
+ "backgroundColor": "#660000",
+ "timeout": 5,
+ "repeat": 1,
+ "scroll": true
+ };
+ });
+
+ it("should set the correct class when location is top of the page", function () {
+ Display.setAlertLocation(0);
+
+ expect(alertContainer.className).toEqual("alert-container top");
+ });
+
+ it("should set the correct class when location is middle of the page", function () {
+ Display.setAlertLocation(1);
+
+ expect(alertContainer.className).toEqual("alert-container middle");
+ });
+
+ it("should set the correct class when location is bottom of the page", function () {
+ Display.setAlertLocation(2);
+
+ expect(alertContainer.className).toEqual("alert-container bottom");
+ });
+});
+
+describe("Display.addAlertToQueue", function () {
+ var alertContainer, alertBackground, alertText, settings;
+
+ beforeEach(function () {
+ document.body.innerHTML = "";
+ alertContainer = _createDiv({"class": "alert-container"});
+ alertBackground = _createDiv({"id": "alert-background", "class": "hide"});
+ alertText = _createDiv({"id": "alert-text", "class": "hide"});
+ settings = {
+ "location": 1,
+ "fontFace": "sans-serif",
+ "fontSize": 40,
+ "fontColor": "#ffffff",
+ "backgroundColor": "#660000",
+ "timeout": 5,
+ "repeat": 1,
+ "scroll": true
+ };
+ });
+
+ it("should add an alert to the queue if one is displaying already", function() {
+ Display._alerts = [];
+ Display._alertState = AlertState.Displaying;
+ var alertObject = {text: "Testing alert queue", settings: settings};
+
+ Display.addAlertToQueue("Testing alert queue", settings);
+
+ expect(Display._alerts.length).toEqual(1);
+ expect(Display._alerts[0]).toEqual(alertObject);
+ });
+});
+
+describe("Display.showNextAlert", function () {
+ var alertContainer, alertBackground, alertText, settings;
+
+ beforeEach(function () {
+ document.body.innerHTML = "";
+ alertContainer = _createDiv({"class": "alert-container"});
+ alertBackground = _createDiv({"id": "alert-background", "class": "hide"});
+ alertText = _createDiv({"id": "alert-text", "class": "hide"});
+ settings = {
+ "location": 1,
+ "fontFace": "sans-serif",
+ "fontSize": 40,
+ "fontColor": "#ffffff",
+ "backgroundColor": "#660000",
+ "timeout": 5,
+ "repeat": 1,
+ "scroll": true
+ };
+ });
+
+ it("should return null if there are no alerts in the queue", function () {
+ Display._alerts = [];
+ Display.showNextAlert();
+
+ expect(Display.showNextAlert()).toBeNull();
+ });
+
+ it("should call the alert function correctly if there is an alert in the queue", function () {
+ Display._alerts.push({text: "Queued Alert", settings: settings});
+ spyOn(Display, "showAlert");
+ Display.showNextAlert();
+
+ expect(Display.showAlert).toHaveBeenCalled();
+ expect(Display.showAlert).toHaveBeenCalledWith("Queued Alert", settings);
+ });
+});
+
+describe("Display.alertTransitionEndEvent", function() {
+ var e = { stopPropagation: function () { } };
+
+ it("should call event.stopPropagation()", function () {
+ spyOn(e, "stopPropagation");
+
+ Display.alertTransitionEndEvent(e);
+
+ expect(e.stopPropagation).toHaveBeenCalled();
+ });
+
+ it("should set the correct state after EntranceTransition", function() {
+ Display._transitionState = TransitionState.EntranceTransition;
+
+ Display.alertTransitionEndEvent(e);
+
+ expect(Display._transitionState).toEqual(TransitionState.NoTransition);
+ });
+
+ it("should set the correct state after ExitTransition, call hideAlert() and showNextAlert()", function() {
+ spyOn(Display, "hideAlert");
+ spyOn(Display, "showNextAlert");
+ Display._transitionState = TransitionState.ExitTransition;
+
+ Display.alertTransitionEndEvent(e);
+
+ expect(Display._transitionState).toEqual(TransitionState.NoTransition);
+ expect(Display.hideAlert).toHaveBeenCalled();
+ expect(Display.showNextAlert).toHaveBeenCalled();
+ });
+});
+
+describe("Display.alertAnimationEndEvent", function () {
+ var e = { stopPropagation: function () { } };
+
+ it("should call the hideAlert method", function() {
+ spyOn(Display, "hideAlert");
+
+ Display.alertAnimationEndEvent(e);
+
+ expect(Display.hideAlert).toHaveBeenCalled();
+ });
});
describe("Display.addTextSlide", function () {
@@ -181,7 +479,7 @@
it("should update an existing slide", function () {
var verse = "v1",
- text = "Amazing grace, how sweet the sound\nThat saved a wretch like me",
+ text = "Amazing grace, how sweet the sound\nThat saved a wretch like me",
footer = "Public Domain";
Display.addTextSlide(verse, "Amazing grace,\nhow sweet the sound", footer, false);
spyOn(Display, "reinit");
@@ -249,6 +547,7 @@
'font_main_outline_color': 'red'
};
spyOn(Display, "reinit");
+ spyOn(Reveal, "slide");
Display.setTextSlides(slides);
Display.setTheme(theme);
Follow ups