From 3af3068027cc8ccef3938edd1d6f77d54cc5db0e Mon Sep 17 00:00:00 2001 From: Raoul Snyman Date: Wed, 11 Sep 2019 21:41:30 -0700 Subject: [PATCH] Fix alerts to scroll properly, and simplify how everything works --- openlp/core/display/html/display.css | 170 ++++++++------- openlp/core/display/html/display.html | 33 +-- openlp/core/display/html/display.js | 295 +++++++++++++------------- openlp/core/display/webengine.py | 6 + openlp/core/display/window.py | 3 +- tests/js/test_display.js | 60 ++---- 6 files changed, 260 insertions(+), 307 deletions(-) diff --git a/openlp/core/display/html/display.css b/openlp/core/display/html/display.css index 111eaaadc..d1af3a902 100644 --- a/openlp/core/display/html/display.css +++ b/openlp/core/display/html/display.css @@ -1,80 +1,90 @@ -@keyframes alert-scrolling-text { - 0% { transform: translateX(100%); opacity: 1; } - 99% { opacity: 1; } - 100% { transform: translateX(-101%); opacity: 0;} -} - -/* ALERT BACKGROUND STYLING */ -.bg-default { - position: absolute; - margin: 0; - padding: 0; - left: 0; - z-index: 11; - width: 100%; - height: 0; - min-height: 0; - overflow: hidden; - transform: translate(0,0); - transition: min-height 1s ease-out .5s; - white-space: nowrap; - display: flex; - flex-direction: row; - align-items: center; - /* align-content: center; */ -} - -.bg-default span { - display: inline-block; - padding-left: 120%; -} - -.show-bg { - /* height: auto; */ - min-height: 25%; - transition: min-height 1s ease-in .5s; -} - -.middle { - align-items: center; -} - -.alert-container { - position: absolute; - display: flex; - flex-direction: row; - height: 100vh; - width: 100vw; -} - -.top { - align-items: flex-start; -} - -.bottom { - align-items: flex-end; -} - -/* ALERT TEXT STYLING */ -#alert { - z-index: 100; - overflow: visible; - color: #ffffff; - font-size: 40pt; - padding: 0; - margin: 0; - opacity: 0; - transition: opacity .5s linear; -} - -#alert.hide-text { - opacity: 0; -} - -#alert.show-text { - transform: none; - transition: none; - animation: none; - padding: auto 5px; - opacity: 1; -} +@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; +} diff --git a/openlp/core/display/html/display.html b/openlp/core/display/html/display.html index 2968641c7..4a5870bb0 100644 --- a/openlp/core/display/html/display.html +++ b/openlp/core/display/html/display.html @@ -2,43 +2,20 @@ Display Window - - - + + -
-
Testing alerts
+
Testing alerts
-
+
-
+ diff --git a/openlp/core/display/html/display.js b/openlp/core/display/html/display.js index aeefadd96..e04daf8fe 100644 --- a/openlp/core/display/html/display.js +++ b/openlp/core/display/html/display.js @@ -83,12 +83,12 @@ var AlertLocation = { * Alert state enumeration */ var AlertState = { - Displaying: "displaying", + Displaying: "displaying", NotDisplaying: "notDisplaying" } /** - * Alert delay enumeration + * Alert delay enumeration */ var AlertDelay = { FiftyMilliseconds: 50, @@ -161,6 +161,60 @@ function _prepareText(text) { return "

" + _nl2br(text) + "

"; } +/** + * 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)); + } +} + +function _quote(text) { + return '"' + text + '"'; +} + +function _increaseSize(size) { + var number = size.match(/[\d]+/g); + var newNumber = parseInt(number) + 2; + return size.replace(number, newNumber.toString()); +} + /** * An audio player with a play list */ @@ -403,94 +457,105 @@ var Display = { Display.reinit(); }, /** - * Display an alert + * Display an alert. If there's an alert already showing, add this one to the queue * @param {string} text - The alert text - * @param {string} JSON object - The settings for the alert object e.g '{"backgroundColor": "rgb(255, 85, 0)", - * "location": 1, "fontFace": "Open Sans Condensed", "fontSize": 90, "fontColor": "rgb(255, 255, 255)", - * "timeout": 10, "repeat": 2, "scroll": true}' + * @param {Object} JSON object - The settings for the alert object */ - alert: function (text, alertSettings) { - var alertBackground = $('#alert-background')[0]; - var alertText = $('#alert')[0]; + alert: function (text, settings) { if (text == "") { return null; } - else { - if (this._alertState === AlertState.Displaying) { - Display.addAlertToQueue(text, alertSettings); - } - else - { - var settings = JSON.parse(alertSettings); - this._alertSettings = settings; - Display.setAlertText(text, settings.fontColor, settings.fontFace, settings.fontSize); - Display.setAlertLocation(settings.location); - /* 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); - - Display.showAlertBackground(settings.backgroundColor); - } + if (Display._alertState === AlertState.Displaying) { + console.debug("Adding to queue"); + Display.addAlertToQueue(text, settings); + } + else { + console.debug("Displaying immediately"); + Display.showAlert(text, settings); } - }, - /** - * Add an alert to the alert queue - * @param {string} text - The alert text to be displayed - * @param {string} setttings - JSON object containing the settings for the alert - */ - addAlertToQueue: function (text, settings) { - var alertObject = {text: text, settings: settings}; - this._alerts.push(JSON.stringify(alertObject)); - return null; }, /** - * Set Alert Text - * @param {string} text - The alert text to display + * 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: _quote(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); + /* 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 */ - setAlertText: function (text, color, fontFace, fontSize) { - var alertText = $("#alert")[0]; - alertText.textContent = text; - alertText.style.color = color; - alertText.style.fontFamily = fontFace; - alertText.style.fontSize = fontSize + "pt"; - }, + hideAlert: function () { + var alertBackground = $('#alert-background')[0]; + var alertText = $('#alert-text')[0]; + 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}); + return null; + }, /** * 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) { + console.debug("Transition end event reached: " + Display._transitionState); + if (Display._transitionState === TransitionState.EntranceTransition) { Display._transitionState = TransitionState.NoTransition; - Display.showAlertText(Display._alertSettings); } - else if (Display._transitionState === TransitionState.ExitTransition) { - Display._transitionState = TransitionState.NoTransition; - Display.removeAlertLocation(Display._alertSettings.location); - Display.clearAlertSettings(); + 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.hideAlertText(); - }, - /** - * Start background entrance transition for display of alert - * @param {string} hex_color - The background color of the alert - */ - showAlertBackground: function (bg_color) { - var alertBackground = $("#alert-background")[0]; - alertBackground.classList.add("show-bg"); - alertBackground.style.backgroundColor = bg_color; - this._transitionState = TransitionState.EntranceTransition; + e.stopPropagation(); + Display.hideAlert(); }, /** * Set the location of the alert @@ -498,102 +563,34 @@ var Display = { */ 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"); + alertContainer.classList.add("top"); break; case AlertLocation.Middle: - alertContainer.classList.add("middle"); + alertContainer.classList.add("middle"); break; case AlertLocation.Bottom: default: - alertContainer.classList.add("bottom"); + alertContainer.classList.add("bottom"); break; - } - }, - /** - * Remove the location class set after displaying the alert - * @param {int} location - Integer number with the location of the alert on screen - */ - removeAlertLocation: function (location) { - var alertContainer = $(".alert-container")[0]; - console.debug("The value of location for removal is: " + location); - - switch (location) { - case AlertLocation.Top: - alertContainer.classList.remove("top"); - break; - case AlertLocation.Middle: - alertContainer.classList.remove("middle"); - break; - case AlertLocation.Bottom: - default: - alertContainer.classList.remove("bottom"); - break; - } - }, - /** - * Hide the alert background after the alert has been shown - */ - hideAlertBackground: function () { - var alertBackground = $("#alert-background")[0]; - alertBackground.classList.remove("show-bg"); - this._transitionState = TransitionState.ExitTransition; - this._alertState = AlertState.NotDisplaying; - }, - /** - * Sets the alert text styles correctly after the entrance transition has ended. - * @param {json} settings object - The settings to use for the animation - */ - showAlertText: function (settings) { - var alertText = $("#alert")[0]; - - if (settings.scroll) { - var animationSettings = "alert-scrolling-text " + settings.timeout + - "s linear 0.6s " + settings.repeat + " normal"; - alertText.style.animation = animationSettings; - Display._animationState = AnimationState.ScrollingText; } - else { - Display._animationState = AnimationState.NonScrollingText; - alertText.classList.add("show-text"); - setTimeout (function () { - Display._animationState = AnimationState.NoAnimation; - alertText.classList.add("hide-text"); - alertText.classList.remove("show-text"); - Display.hideAlertText(); - }, settings.timeout * AlertDelay.OneSecond); - } }, /** - * Reset styling and hide the alert text after displaying the animation - */ - hideAlertText: function () { - var alertText = $('#alert')[0]; - Display._animationState = AnimationState.NoAnimation; - alertText.style.animation = ""; - Display.hideAlertBackground(); - }, - /** * Display the next alert in the queue */ - showNextAlert: function () { + showNextAlert: function () { if (Display._alerts.length > 0) { - var alertObject = JSON.parse(this._alerts.shift()); - this._alertState = AlertState.DisplayingFromQueue; - Display.alert(alertObject.text, alertObject.settings); - } - else { - return null; + var alertObject = Display._alerts.shift(); + Display._alertState = AlertState.DisplayingFromQueue; + Display.showAlert(alertObject.text, alertObject.settings); } }, - /** - * Clears the alert settings after displaying an alert - */ - clearAlertSettings: function () { - this._alertSettings = {}; - }, /** * Add a slide. If the slide exists but the HTML is different, update the slide. * @param {string} verse - The verse number, e.g. "v1" @@ -991,7 +988,7 @@ var Display = { 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+"%"; diff --git a/openlp/core/display/webengine.py b/openlp/core/display/webengine.py index fbec9c915..defc27066 100644 --- a/openlp/core/display/webengine.py +++ b/openlp/core/display/webengine.py @@ -27,6 +27,8 @@ import logging from PyQt5 import QtCore, QtWebEngineWidgets, QtWidgets +from openlp.core.common.applocation import AppLocation + LOG_LEVELS = { QtWebEngineWidgets.QWebEnginePage.InfoMessageLevel: logging.INFO, @@ -46,6 +48,10 @@ class WebEnginePage(QtWebEngineWidgets.QWebEnginePage): """ 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)) diff --git a/openlp/core/display/window.py b/openlp/core/display/window.py index 8bf846b14..46da2ddda 100644 --- a/openlp/core/display/window.py +++ b/openlp/core/display/window.py @@ -401,5 +401,4 @@ class DisplayWindow(QtWidgets.QWidget): """ Set an alert """ - self.run_javascript('Display.alert("{text}", \'{settings}\');'.format(text=text, settings=settings)) - # TODO: Add option to prevent scrolling + self.run_javascript('Display.alert("{text}", {settings});'.format(text=text, settings=settings)) diff --git a/tests/js/test_display.js b/tests/js/test_display.js index 1a83edd80..6f56eb87c 100644 --- a/tests/js/test_display.js +++ b/tests/js/test_display.js @@ -165,13 +165,13 @@ describe("Display.alert", function () { alertBackground.setAttribute("id", "alert-background"); alertContainer.appendChild(alertBackground); alertText = document.createElement("span"); - alertText.setAttribute("id","alert"); + alertText.setAttribute("id","alert-text"); alertBackground.appendChild(alertText); - settings = '{ \ - "location": 1, "fontFace": "Segoe UI, Tahoma, Geneva, Verdana, sans-serif", \ - "fontSize": 40, "fontColor": "#ffffff", "backgroundColor": "#660000", \ - "timeout": 5, "repeat": 1, "scroll": true \ - }'; + settings = { + "location": 1, "fontFace": "Segoe UI, Tahoma, Geneva, Verdana, sans-serif", + "fontSize": 40, "fontColor": "#ffffff", "backgroundColor": "#660000", + "timeout": 5, "repeat": 1, "scroll": true + }; }); it("should return null if called without any text", function () { @@ -179,29 +179,23 @@ describe("Display.alert", function () { }); it("should set the correct alert text", function () { - spyOn(Display, "setAlertText"); - spyOn(Display, "setAlertLocation"); + spyOn(Display, "showAlert"); + Display.alert("OPEN-LP-3.0 Alert Test", settings); - - expect(Display.setAlertText).toHaveBeenCalled(); - expect(Display.setAlertLocation).toHaveBeenCalled(); + + expect(Display.showAlert).toHaveBeenCalled(); }); it("should call the addAlertToQueue method if an alert is displaying", function () { + var text = "Testing alert queue"; spyOn(Display, "addAlertToQueue"); Display._alerts = []; Display._alertState = AlertState.Displaying; - var text = "Testing alert queue"; Display.alert(text, settings); expect(Display.addAlertToQueue).toHaveBeenCalledWith(text, settings); - }); - - it("should set the alert settings correctly", function() { - Display.alert("Testing settings", settings); - - expect(Display._alertSettings).toEqual(JSON.parse(settings)); + expect(Display._alerts[0]).toBe({"alert": text, "settings": settings}); }); }); @@ -213,7 +207,6 @@ describe("Display.showAlertBackground", function () { bg_color = "rgb(102, 0, 0)"; alertBackground = document.createElement("div"); alertBackground.setAttribute("id", "alert-background"); - alertBackground.setAttribute("class", "bg-default"); document.body.appendChild(alertBackground); }); @@ -293,35 +286,6 @@ describe("Display.setAlertLocation", function() { }); }); -describe("Display.removeAlertLocation", function () { - beforeEach(function() { - document.body.innerHTML = ""; - alertContainer = document.createElement("div"); - alertContainer.setAttribute("class", "alert-container"); - document.body.appendChild(alertContainer); - }); - it("should remove the correct class when location is top of the page", function () { - alertContainer.classList.add("top"); - Display.removeAlertLocation(0); - - expect(alertContainer.className).toEqual("alert-container"); - }); - - it("should remove the correct class when location is middle of the page", function () { - alertContainer.classList.add("middle"); - Display.removeAlertLocation(1); - - expect(alertContainer.className).toEqual("alert-container"); - }); - - it("should remove the correct class when location is bottom of the page", function () { - alertContainer.classList.add("bottom"); - Display.removeAlertLocation(2); - - expect(alertContainer.className).toEqual("alert-container"); - }); -}); - describe("Display.showAlertText", function () { var alertText, settings; beforeEach(function () {