Fix alerts to scroll properly, and simplify how everything works

This commit is contained in:
Raoul Snyman 2019-09-11 21:41:30 -07:00
parent 9c078cce04
commit 3af3068027
6 changed files with 260 additions and 307 deletions

View File

@ -1,80 +1,90 @@
@keyframes alert-scrolling-text { @keyframes alert-scrolling-text {
0% { transform: translateX(100%); opacity: 1; } 0% {
99% { opacity: 1; } opacity: 1;
100% { transform: translateX(-101%); opacity: 0;} transform: translateX(100%);
} }
99% {
/* ALERT BACKGROUND STYLING */ opacity: 1;
.bg-default { }
position: absolute; 100% {
margin: 0; opacity: 0;
padding: 0; transform: translateX(-101%);
left: 0; }
z-index: 11; }
width: 100%;
height: 0; body {
min-height: 0; background: transparent !important;
overflow: hidden; color: rgb(255, 255, 255) !important;
transform: translate(0,0); }
transition: min-height 1s ease-out .5s;
white-space: nowrap; sup {
display: flex; vertical-align: super !important;
flex-direction: row; font-size: smaller !important;
align-items: center; }
/* align-content: center; */
} .reveal .slides > section,
.reveal .slides > section > section {
.bg-default span { padding: 0;
display: inline-block; }
padding-left: 120%;
} .reveal > .backgrounds > .present {
visibility: hidden !important;
.show-bg { }
/* height: auto; */
min-height: 25%; #global-background {
transition: min-height 1s ease-in .5s; display: block;
} visibility: visible;
z-index: -1;
.middle { }
align-items: center;
} .alert-container {
position: absolute;
.alert-container { display: flex;
position: absolute; flex-direction: row;
display: flex; height: 100vh;
flex-direction: row; width: 100vw;
height: 100vh; }
width: 100vw;
} .hide {
opacity: 0 !important;
.top { transition: opacity 0.5s ease;
align-items: flex-start; }
}
.show {
.bottom { opacity: 1 !important;
align-items: flex-end; transition: opacity 0.5s ease;
} }
/* ALERT TEXT STYLING */ .middle {
#alert { align-items: center;
z-index: 100; }
overflow: visible;
color: #ffffff; .top {
font-size: 40pt; align-items: flex-start;
padding: 0; }
margin: 0;
opacity: 0; .bottom {
transition: opacity .5s linear; align-items: flex-end;
} }
#alert.hide-text { #alert-background {
opacity: 0; left: 0;
} margin: 0;
opacity: 0;
#alert.show-text { overflow: hidden;
transform: none; padding: 0.5em 0;
transition: none; position: absolute;
animation: none; transition: opacity 0.5s ease;
padding: auto 5px; white-space: nowrap;
opacity: 1; 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;
}

View File

@ -2,43 +2,20 @@
<html> <html>
<head> <head>
<title>Display Window</title> <title>Display Window</title>
<link href="reveal.css" rel="stylesheet"> <link type="text/css" rel="stylesheet" href="reveal.css">
<style type="text/css"> <link type="text/css" rel="stylesheet" href="display.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 rel="stylesheet" type="text/css" href="display.css"></link>
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script> <script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
<script type="text/javascript" src="reveal.js"></script> <script type="text/javascript" src="reveal.js"></script>
<script type="text/javascript" src="display.js"></script> <script type="text/javascript" src="display.js"></script>
</head> </head>
<body> <body>
<div class="alert-container"> <div class="alert-container">
<div id="alert-background" class="bg-default"><span id="alert">Testing alerts</span></div> <div id="alert-background" class="hide"><div id="alert-text" class="hide">Testing alerts</div></div>
</div> </div>
<div class="reveal"> <div class="reveal">
<div id="global-background" class="slide-background present" data-loaded="true"></div> <div id="global-background" class="slide-background present" data-loaded="true"></div>
<div class="slides"></div> <div class="slides"></div>
<div class="footer"></div> <div class="footer"></div>
</div> </div>
</body> </body>
</html> </html>

View File

@ -83,12 +83,12 @@ var AlertLocation = {
* Alert state enumeration * Alert state enumeration
*/ */
var AlertState = { var AlertState = {
Displaying: "displaying", Displaying: "displaying",
NotDisplaying: "notDisplaying" NotDisplaying: "notDisplaying"
} }
/** /**
* Alert delay enumeration * Alert delay enumeration
*/ */
var AlertDelay = { var AlertDelay = {
FiftyMilliseconds: 50, FiftyMilliseconds: 50,
@ -161,6 +161,60 @@ function _prepareText(text) {
return "<p>" + _nl2br(text) + "</p>"; return "<p>" + _nl2br(text) + "</p>";
} }
/**
* 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 * An audio player with a play list
*/ */
@ -403,94 +457,105 @@ var Display = {
Display.reinit(); 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} text - The alert text
* @param {string} JSON object - The settings for the alert object e.g '{"backgroundColor": "rgb(255, 85, 0)", * @param {Object} JSON object - The settings for the alert object
* "location": 1, "fontFace": "Open Sans Condensed", "fontSize": 90, "fontColor": "rgb(255, 255, 255)",
* "timeout": 10, "repeat": 2, "scroll": true}'
*/ */
alert: function (text, alertSettings) { alert: function (text, settings) {
var alertBackground = $('#alert-background')[0];
var alertText = $('#alert')[0];
if (text == "") { if (text == "") {
return null; return null;
} }
else { if (Display._alertState === AlertState.Displaying) {
if (this._alertState === AlertState.Displaying) { console.debug("Adding to queue");
Display.addAlertToQueue(text, alertSettings); Display.addAlertToQueue(text, settings);
} }
else else {
{ console.debug("Displaying immediately");
var settings = JSON.parse(alertSettings); Display.showAlert(text, settings);
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);
}
} }
},
/**
* 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 * Show the alert on the screen
* @param {string} text - The alert text to display * @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) { hideAlert: function () {
var alertText = $("#alert")[0]; var alertBackground = $('#alert-background')[0];
alertText.textContent = text; var alertText = $('#alert-text')[0];
alertText.style.color = color; alertText.classList.replace("show", "hide");
alertText.style.fontFamily = fontFace; alertBackground.classList.replace("show", "hide");
alertText.style.fontSize = fontSize + "pt"; 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 * The alertTransitionEndEvent called after a transition has ended
*/ */
alertTransitionEndEvent: function (e) { alertTransitionEndEvent: function (e) {
e.stopPropagation(); e.stopPropagation();
console.debug("Transition end event reached: " + Display._transitionState); console.debug("Transition end event reached: " + Display._transitionState);
if (Display._transitionState === TransitionState.EntranceTransition) { if (Display._transitionState === TransitionState.EntranceTransition) {
Display._transitionState = TransitionState.NoTransition; Display._transitionState = TransitionState.NoTransition;
Display.showAlertText(Display._alertSettings);
} }
else if (Display._transitionState === TransitionState.ExitTransition) { else if (Display._transitionState === TransitionState.ExitTransition) {
Display._transitionState = TransitionState.NoTransition; Display._transitionState = TransitionState.NoTransition;
Display.removeAlertLocation(Display._alertSettings.location); Display.hideAlert();
Display.clearAlertSettings();
Display.showNextAlert(); Display.showNextAlert();
} }
}, },
/** /**
* The alertAnimationEndEvent called after an animation has ended * The alertAnimationEndEvent called after an animation has ended
*/ */
alertAnimationEndEvent: function (e) { alertAnimationEndEvent: function (e) {
e.stopPropagation(); e.stopPropagation();
Display.hideAlertText(); Display.hideAlert();
},
/**
* 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;
}, },
/** /**
* Set the location of the alert * Set the location of the alert
@ -498,102 +563,34 @@ var Display = {
*/ */
setAlertLocation: function (location) { setAlertLocation: function (location) {
var alertContainer = $(".alert-container")[0]; 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) { switch (location) {
case AlertLocation.Top: case AlertLocation.Top:
alertContainer.classList.add("top"); alertContainer.classList.add("top");
break; break;
case AlertLocation.Middle: case AlertLocation.Middle:
alertContainer.classList.add("middle"); alertContainer.classList.add("middle");
break; break;
case AlertLocation.Bottom: case AlertLocation.Bottom:
default: default:
alertContainer.classList.add("bottom"); alertContainer.classList.add("bottom");
break; 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 * Display the next alert in the queue
*/ */
showNextAlert: function () { showNextAlert: function () {
if (Display._alerts.length > 0) { if (Display._alerts.length > 0) {
var alertObject = JSON.parse(this._alerts.shift()); var alertObject = Display._alerts.shift();
this._alertState = AlertState.DisplayingFromQueue; Display._alertState = AlertState.DisplayingFromQueue;
Display.alert(alertObject.text, alertObject.settings); Display.showAlert(alertObject.text, alertObject.settings);
}
else {
return null;
} }
}, },
/**
* 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. * 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} verse - The verse number, e.g. "v1"
@ -991,7 +988,7 @@ var Display = {
return videoTypes; 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) { setScale: function(scale) {
document.body.style.zoom = scale+"%"; document.body.style.zoom = scale+"%";

View File

@ -27,6 +27,8 @@ import logging
from PyQt5 import QtCore, QtWebEngineWidgets, QtWidgets from PyQt5 import QtCore, QtWebEngineWidgets, QtWidgets
from openlp.core.common.applocation import AppLocation
LOG_LEVELS = { LOG_LEVELS = {
QtWebEngineWidgets.QWebEnginePage.InfoMessageLevel: logging.INFO, 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 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, log.log(LOG_LEVELS[level], '{source_id}:{line_number} {message}'.format(source_id=source_id,
line_number=line_number, line_number=line_number,
message=message)) message=message))

View File

@ -401,5 +401,4 @@ class DisplayWindow(QtWidgets.QWidget):
""" """
Set an alert Set an alert
""" """
self.run_javascript('Display.alert("{text}", \'{settings}\');'.format(text=text, settings=settings)) self.run_javascript('Display.alert("{text}", {settings});'.format(text=text, settings=settings))
# TODO: Add option to prevent scrolling

View File

@ -165,13 +165,13 @@ describe("Display.alert", function () {
alertBackground.setAttribute("id", "alert-background"); alertBackground.setAttribute("id", "alert-background");
alertContainer.appendChild(alertBackground); alertContainer.appendChild(alertBackground);
alertText = document.createElement("span"); alertText = document.createElement("span");
alertText.setAttribute("id","alert"); alertText.setAttribute("id","alert-text");
alertBackground.appendChild(alertText); alertBackground.appendChild(alertText);
settings = '{ \ settings = {
"location": 1, "fontFace": "Segoe UI, Tahoma, Geneva, Verdana, sans-serif", \ "location": 1, "fontFace": "Segoe UI, Tahoma, Geneva, Verdana, sans-serif",
"fontSize": 40, "fontColor": "#ffffff", "backgroundColor": "#660000", \ "fontSize": 40, "fontColor": "#ffffff", "backgroundColor": "#660000",
"timeout": 5, "repeat": 1, "scroll": true \ "timeout": 5, "repeat": 1, "scroll": true
}'; };
}); });
it("should return null if called without any text", function () { 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 () { it("should set the correct alert text", function () {
spyOn(Display, "setAlertText"); spyOn(Display, "showAlert");
spyOn(Display, "setAlertLocation");
Display.alert("OPEN-LP-3.0 Alert Test", settings); Display.alert("OPEN-LP-3.0 Alert Test", settings);
expect(Display.setAlertText).toHaveBeenCalled(); expect(Display.showAlert).toHaveBeenCalled();
expect(Display.setAlertLocation).toHaveBeenCalled();
}); });
it("should call the addAlertToQueue method if an alert is displaying", function () { it("should call the addAlertToQueue method if an alert is displaying", function () {
var text = "Testing alert queue";
spyOn(Display, "addAlertToQueue"); spyOn(Display, "addAlertToQueue");
Display._alerts = []; Display._alerts = [];
Display._alertState = AlertState.Displaying; Display._alertState = AlertState.Displaying;
var text = "Testing alert queue";
Display.alert(text, settings); Display.alert(text, settings);
expect(Display.addAlertToQueue).toHaveBeenCalledWith(text, settings); expect(Display.addAlertToQueue).toHaveBeenCalledWith(text, settings);
}); expect(Display._alerts[0]).toBe({"alert": text, "settings": settings});
it("should set the alert settings correctly", function() {
Display.alert("Testing settings", settings);
expect(Display._alertSettings).toEqual(JSON.parse(settings));
}); });
}); });
@ -213,7 +207,6 @@ describe("Display.showAlertBackground", function () {
bg_color = "rgb(102, 0, 0)"; bg_color = "rgb(102, 0, 0)";
alertBackground = document.createElement("div"); alertBackground = document.createElement("div");
alertBackground.setAttribute("id", "alert-background"); alertBackground.setAttribute("id", "alert-background");
alertBackground.setAttribute("class", "bg-default");
document.body.appendChild(alertBackground); 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 () { describe("Display.showAlertText", function () {
var alertText, settings; var alertText, settings;
beforeEach(function () { beforeEach(function () {