Based on ic90's code, this just simplifies some of the code, and streamlines how things work, plus a couple of CSS fixes.

bzr-revno: 2904
This commit is contained in:
Raoul Snyman 2019-09-15 17:22:22 +02:00 committed by Tomas Groth
commit 9d16ae2b06
9 changed files with 739 additions and 60 deletions

View File

@ -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;
}

View File

@ -2,34 +2,16 @@
<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>
<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 id="alert-background" class="hide"><div id="alert-text" class="hide">Testing alerts</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>

View File

@ -52,6 +52,50 @@ var AudioState = {
Stopped: "stopped" Stopped: "stopped"
}; };
/**
* 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 * Return an array of elements based on the selector query
* @param {string} selector - The selector to find elements * @param {string} selector - The selector to find elements
@ -117,6 +161,50 @@ 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));
}
}
/** /**
* An audio player with a play list * An audio player with a play list
*/ */
@ -234,7 +322,12 @@ AudioPlayer.prototype.stop = function () {
* The Display object is what we use from OpenLP * The Display object is what we use from OpenLP
*/ */
var Display = { var Display = {
_alerts: [],
_slides: {}, _slides: {},
_alertSettings: {},
_alertState: AlertState.NotDisplaying,
_transitionState: TransitionState.NoTransition,
_animationState: AnimationState.NoAnimation,
_revealConfig: { _revealConfig: {
margin: 0.0, margin: 0.0,
minScale: 1.0, minScale: 1.0,
@ -355,21 +448,149 @@ 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 {int} location - The location of the text (top, middle or bottom) * @param {Object} JSON object - The settings for the alert object
*/ */
alert: function (text, location) { alert: function (text, settings) {
console.debug(" alert text: " + text, ", location: " + location); if (text == "") {
/* return null;
* 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. if (Display._alertState === AlertState.Displaying) {
*/ console.debug("Adding to queue");
return; Display.addAlertToQueue(text, settings);
}, }
else {
console.debug("Displaying immediately");
Display.showAlert(text, settings);
}
},
/** /**
* Add a slides. If the slide exists but the HTML is different, update the slide. * 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} verse - The verse number, e.g. "v1"
* @param {string} text - The HTML for the verse, e.g. "line1<br>line2" * @param {string} text - The HTML for the verse, e.g. "line1<br>line2"
* @param {string} footer_text - The HTML for the footer" * @param {string} footer_text - The HTML for the footer"
@ -770,7 +991,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

@ -399,8 +399,8 @@ class DisplayWindow(QtWidgets.QWidget):
self.scale = scale self.scale = scale
self.run_javascript('Display.setScale({scale});'.format(scale=scale * 100)) self.run_javascript('Display.setScale({scale});'.format(scale=scale * 100))
def alert(self, text, location): def alert(self, text, settings):
""" """
Set an alert 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))

View File

@ -126,7 +126,9 @@ __default_settings__ = {
'alerts/location': AlertLocation.Bottom, 'alerts/location': AlertLocation.Bottom,
'alerts/background color': '#660000', 'alerts/background color': '#660000',
'alerts/font color': '#ffffff', 'alerts/font color': '#ffffff',
'alerts/timeout': 5 'alerts/timeout': 10,
'alerts/repeat': 1,
'alerts/scroll': True
} }

View File

@ -23,7 +23,9 @@
The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of the plugin which manages storing and The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of the plugin which manages storing and
displaying of alerts. 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.i18n import translate
from openlp.core.common.mixins import LogMixin, RegistryProperties from openlp.core.common.mixins import LogMixin, RegistryProperties
@ -83,8 +85,23 @@ class AlertsManager(QtCore.QObject, RegistryBase, LogMixin, RegistryProperties):
not Settings().value('core/display on monitor')): not Settings().value('core/display on monitor')):
return return
text = self.alert_list.pop(0) 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))
def timerEvent(self, event): def timerEvent(self, event):
""" """
@ -98,3 +115,13 @@ class AlertsManager(QtCore.QObject, RegistryBase, LogMixin, RegistryProperties):
self.killTimer(self.timer_id) self.killTimer(self.timer_id)
self.timer_id = 0 self.timer_id = 0
self.generate_alert() 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()) + ")"

View File

@ -47,35 +47,56 @@ class AlertsTab(SettingsTab):
self.font_layout.addRow(self.font_label, self.font_combo_box) 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 = QtWidgets.QLabel(self.font_group_box)
self.font_color_label.setObjectName('font_color_label') 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 = ColorButton(self.font_group_box)
self.font_color_button.setObjectName('font_color_button') self.font_color_button.setObjectName('font_color_button')
self.color_layout.addWidget(self.font_color_button) self.font_layout.addRow(self.font_color_label, 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_size_label = QtWidgets.QLabel(self.font_group_box) self.font_size_label = QtWidgets.QLabel(self.font_group_box)
self.font_size_label.setObjectName('font_size_label') self.font_size_label.setObjectName('font_size_label')
self.font_size_spin_box = QtWidgets.QSpinBox(self.font_group_box) self.font_size_spin_box = QtWidgets.QSpinBox(self.font_group_box)
self.font_size_spin_box.setObjectName('font_size_spin_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.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_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.setMaximum(180)
self.timeout_spin_box.setObjectName('timeout_spin_box') 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, self.vertical_combo_box = create_valign_selection_widgets(self.font_group_box)
self.vertical_label.setObjectName('vertical_label') self.vertical_label.setObjectName('vertical_label')
self.vertical_combo_box.setObjectName('vertical_combo_box') self.vertical_combo_box.setObjectName('vertical_combo_box')
self.font_layout.addRow(self.vertical_label, self.vertical_combo_box) self.settings_layout.addRow(self.vertical_label, self.vertical_combo_box)
self.left_layout.addWidget(self.font_group_box) self.left_layout.addWidget(self.settings_group_box)
self.left_layout.addStretch() self.left_layout.addStretch()
self.preview_group_box = QtWidgets.QGroupBox(self.right_column) self.preview_group_box = QtWidgets.QGroupBox(self.right_column)
self.preview_group_box.setObjectName('preview_group_box') self.preview_group_box.setObjectName('preview_group_box')
@ -92,16 +113,22 @@ class AlertsTab(SettingsTab):
self.font_combo_box.activated.connect(self.on_font_combo_box_clicked) 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.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.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): 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_label.setText(translate('AlertsPlugin.AlertsTab', 'Font name:'))
self.font_color_label.setText(translate('AlertsPlugin.AlertsTab', 'Font color:')) self.font_color_label.setText(translate('AlertsPlugin.AlertsTab', 'Font color:'))
self.background_color_label.setText(UiStrings().BackgroundColorColon) self.background_color_label.setText(UiStrings().BackgroundColorColon)
self.font_size_label.setText(translate('AlertsPlugin.AlertsTab', 'Font size:')) self.font_size_label.setText(translate('AlertsPlugin.AlertsTab', 'Font size:'))
self.font_size_spin_box.setSuffix(' {unit}'.format(unit=UiStrings().FontSizePtUnit)) 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_label.setText(translate('AlertsPlugin.AlertsTab', 'Alert timeout:'))
self.timeout_spin_box.setSuffix(' {unit}'.format(unit=UiStrings().Seconds)) 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.preview_group_box.setTitle(UiStrings().Preview)
self.font_preview.setText(UiStrings().OpenLP) self.font_preview.setText(UiStrings().OpenLP)
@ -140,6 +167,24 @@ class AlertsTab(SettingsTab):
self.font_size = self.font_size_spin_box.value() self.font_size = self.font_size_spin_box.value()
self.update_display() 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): def load(self):
""" """
Load the settings into the UI. Load the settings into the UI.
@ -152,12 +197,17 @@ class AlertsTab(SettingsTab):
self.background_color = settings.value('background color') self.background_color = settings.value('background color')
self.font_face = settings.value('font face') self.font_face = settings.value('font face')
self.location = settings.value('location') self.location = settings.value('location')
self.repeat = settings.value('repeat')
self.scroll = settings.value('scroll')
settings.endGroup() settings.endGroup()
self.font_size_spin_box.setValue(self.font_size) self.font_size_spin_box.setValue(self.font_size)
self.timeout_spin_box.setValue(self.timeout) self.timeout_spin_box.setValue(self.timeout)
self.font_color_button.color = self.font_color self.font_color_button.color = self.font_color
self.background_color_button.color = self.background_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.vertical_combo_box.setCurrentIndex(self.location)
self.scroll_check_box.setChecked(self.scroll)
font = QtGui.QFont() font = QtGui.QFont()
font.setFamily(self.font_face) font.setFamily(self.font_face)
self.font_combo_box.setCurrentFont(font) self.font_combo_box.setCurrentFont(font)
@ -181,6 +231,8 @@ class AlertsTab(SettingsTab):
settings.setValue('timeout', self.timeout) settings.setValue('timeout', self.timeout)
self.location = self.vertical_combo_box.currentIndex() self.location = self.vertical_combo_box.currentIndex()
settings.setValue('location', self.location) settings.setValue('location', self.location)
settings.setValue('repeat', self.repeat)
settings.setValue('scroll', self.scroll_check_box.isChecked())
settings.endGroup() settings.endGroup()
if self.changed: if self.changed:
self.settings_form.register_post_process('update_display_css') self.settings_form.register_post_process('update_display_css')

View File

@ -27,6 +27,14 @@ describe("The enumeration object", function () {
it("AudioState should exist", function () { it("AudioState should exist", function () {
expect(AudioState).toBeDefined(); expect(AudioState).toBeDefined();
}); });
it("TransitionState should exist", function(){
expect(TransitionState).toBeDefined();
});
it("AnimationState should exist", function(){
expect(AnimationState).toBeDefined();
});
}); });
describe("The function", function () { describe("The function", function () {
@ -141,6 +149,296 @@ describe("The Display object", function () {
Display.goToSlide("v1"); Display.goToSlide("v1");
expect(Reveal.slide).toHaveBeenCalledWith(0); 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 () { describe("Display.addTextSlide", function () {
@ -181,7 +479,7 @@ describe("Display.addTextSlide", function () {
it("should update an existing slide", function () { it("should update an existing slide", function () {
var verse = "v1", 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"; footer = "Public Domain";
Display.addTextSlide(verse, "Amazing grace,\nhow sweet the sound", footer, false); Display.addTextSlide(verse, "Amazing grace,\nhow sweet the sound", footer, false);
spyOn(Display, "reinit"); spyOn(Display, "reinit");
@ -249,6 +547,7 @@ describe("Display.setTextSlides", function () {
'font_main_outline_color': 'red' 'font_main_outline_color': 'red'
}; };
spyOn(Display, "reinit"); spyOn(Display, "reinit");
spyOn(Reveal, "slide");
Display.setTextSlides(slides); Display.setTextSlides(slides);
Display.setTheme(theme); Display.setTheme(theme);