Merge branch 'between-item-transitions' into 'master'

Transitions between items

Closes #191

See merge request openlp/openlp!121
This commit is contained in:
Tim Bentley 2020-03-05 20:35:25 +00:00
commit b51829d4d6
13 changed files with 543 additions and 270 deletions

View File

@ -333,6 +333,7 @@ class Settings(QtCore.QSettings):
'themes/last directory import': None,
'themes/theme level': ThemeLevel.Global,
'themes/wrap footer': False,
'themes/item transitions': False,
'user interface/live panel': True,
'user interface/live splitter geometry': QtCore.QByteArray(),
'user interface/lock panel': True,

View File

@ -22,14 +22,53 @@ sup {
font-size: smaller !important;
}
body,
.reveal .slides,
.reveal .footer {
transition: none;
z-index: 2;
}
body.transition,
body.transition .reveal .slides,
body.transition .reveal .footer {
/* Transition speed for content visibility */
transition: opacity 800ms ease-in-out !important;
}
.reveal .slide-background {
opacity: 1;
visibility: visible;
transition: none !important;
z-index: 2;
/* important required to override inline attributes */
background-repeat: no-repeat !important;
background-size: cover !important;
background-position: 50% 50% !important;
}
.reveal .slide-background:nth-child(n+3) {
/* Hide backgrounds beyond the first 2 (prevents ugly transitions in corner case) */
visibility: hidden;
}
.reveal .slide-background.future,
.reveal .slide-background.past {
opacity: 0;
}
body.transition .reveal .slide-background {
transition: all 800ms ease-in-out !important;
}
.reveal .slides > section,
.reveal .slides > section > section {
padding: 0;
max-height: 100%;
}
.reveal .slides > section.text-slides {
/* Need to override reveal styles to use our text aligment */
.reveal .slides > section {
/* Need to override reveal styles to use our text alignment */
display: flex !important;
flex-direction: column;
word-wrap: break-word;
@ -318,21 +357,24 @@ sup {
.reveal[class*=fade] .slides section.present:not([data-transition]) {
transition-delay: 400ms; }
.reveal .slides section.present[data-transition*=fade],
.reveal .slides section.present[data-transition-speed="fast"][data-transition*=fade],
.reveal[data-transition-speed="fast"][class*=fade] .slides section.present:not([data-transition]) {
transition-delay: 200ms; }
.reveal .slides section.present[data-transition*=fade],
.reveal .slides section.present[data-transition-speed="slow"][data-transition*=fade],
.reveal[data-transition-speed="slow"][class*=fade] .slides section.present:not([data-transition]) {
transition-delay: 800ms; }
.reveal[class*=fade] .slides section {
.reveal[class*=fade] .slides section,
.reveal .slides section[class*=fade] {
transition-duration: 400ms !important; }
.reveal[data-transition-speed="fast"][class*=fade] .slides section {
.reveal[data-transition-speed="fast"][class*=fade] .slides section,
.reveal .slides section[data-transition-speed="fast"][class*=fade] {
transition-duration: 200ms !important; }
.reveal[data-transition-speed="slow"][class*=fade] .slides section {
.reveal[data-transition-speed="slow"][class*=fade] .slides section,
.reveal .slides section[data-transition-speed="slow"][class*=fade] {
transition-duration: 800ms !important; }
.reveal[class*=fade].overview .slides section {

View File

@ -352,6 +352,9 @@ AudioPlayer.prototype.stop = function () {
* The Display object is what we use from OpenLP
*/
var Display = {
_slidesContainer: null,
_footerContainer: null,
_backgroundsContainer: null,
_alerts: [],
_slides: {},
_alertSettings: {},
@ -359,6 +362,8 @@ var Display = {
_transitionState: TransitionState.NoTransition,
_animationState: AnimationState.NoAnimation,
_doTransitions: false,
_doItemTransitions: false,
_themeApplied: true,
_revealConfig: {
margin: 0.0,
minScale: 1.0,
@ -380,9 +385,16 @@ var Display = {
/**
* Start up reveal and do any other initialisation
*/
init: function (doTransitions=false) {
init: function (doTransitions=false, doItemtransitions=false) {
var globalBackground = $("#global-background")[0];
globalBackground.style.cssText = "";
globalBackground.style.setProperty("background", "black");
Display._slidesContainer = $(".slides")[0];
Display._footerContainer = $(".footer")[0];
Display._backgroundsContainer = $(".backgrounds")[0];
Display._doTransitions = doTransitions;
Reveal.initialize(Display._revealConfig);
Display.setItemTransition(doItemtransitions && doTransitions);
displayWatcher.setInitialised(true);
},
/**
@ -394,30 +406,79 @@ var Display = {
Reveal.slide(0);
},
/**
* Set the transition type
* @param {string} transitionType - Can be one of "none", "fade", "slide", "convex", "concave", "zoom"
* @param {string} transitionSpeed - Can be one of "default", "fast", "slow"
* Enable/Disable item transitions
*/
setTransition: function (transitionType, transitionSpeed) {
Reveal.configure({"transition": transitionType, "transitionSpeed": transitionSpeed});
setItemTransition: function (enable) {
Display._doItemTransitions = enable;
var body = $("body")[0];
if (enable) {
body.classList.add("transition");
Reveal.configure({"backgroundTransition": "fade", "transitionSpeed": "default"});
} else {
body.classList.remove("transition");
Reveal.configure({"backgroundTransition": "none"});
}
},
/**
* Clear the current list of slides
*/
clearSlides: function () {
$(".slides")[0].innerHTML = "";
Display._slidesContainer.innerHTML = "";
Display._clearSlidesList();
},
/**
* Clear the current list of slides
*/
_clearSlidesList: function () {
Display._footerContainer.innerHTML = "";
Display._slides = {};
},
/**
* Add new item/slides, replacing the old one
* Clears current list of slides but allows time for a transition animation
* Items are ordered newest to oldest in the slides container
* @param {element} new_slides - New slides to display
* @param {element} is_text - Used to decide if the theme main area constraints should apply
*/
replaceSlides: function (new_slides, is_text=false) {
if (Display._doItemTransitions) {
new_slides.setAttribute('data-transition', "fade");
new_slides.setAttribute('data-transition-speed', "default");
}
new_slides.classList.add("future");
Display.applyTheme(new_slides, is_text);
Display._slidesContainer.prepend(new_slides);
var currentSlide = Reveal.getIndices();
if (Display._doItemTransitions && Display._slidesContainer.children.length >= 2) {
// Set the slide one section ahead so we'll stay on the old slide after reinit
Reveal.slide(1, currentSlide.v);
Display.reinit();
// Timeout to allow time to transition before deleting the old slides
setTimeout (Display._removeLastSection, 5000);
} else {
Reveal.slide(0, currentSlide.v);
Reveal.sync();
Display._removeLastSection();
}
},
/**
* Removes the last slides item if there are more than one
*/
_removeLastSection: function () {
if (Display._slidesContainer.children.length > 1) {
Display._slidesContainer.lastChild.remove();
}
},
/**
* Checks if the present slide content fits within the slide
*/
doesContentFit: function () {
var currSlide = $("section.text-slides");
if (currSlide.length === 0) {
currSlide = $(".slides");
}
currSlide = Display._footerContainer;
} else {
currSlide = currSlide[0];
console.debug("scrollHeight: " + currSlide.scrollHeight + ", clientHeight: " + currSlide.clientHeight);
}
return currSlide.clientHeight >= currSlide.scrollHeight;
},
/**
@ -426,22 +487,17 @@ var Display = {
* @param {string} image - Path to the splash image
*/
setStartupSplashScreen: function(bg_color, image) {
Display.clearSlides();
var globalBackground = $("#global-background")[0];
globalBackground.style.cssText = "";
globalBackground.style.setProperty("background", bg_color);
var slidesDiv = $(".slides")[0];
Display._clearSlidesList();
var section = document.createElement("section");
section.setAttribute("id", 0);
section.setAttribute("data-background", bg_color);
section.setAttribute("style", "height: 100%; width: 100%; position: relative;");
section.setAttribute("style", "height: 100%; width: 100%;");
var img = document.createElement('img');
img.src = image;
img.setAttribute("style", "position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-height: 100%; max-width: 100%");
section.appendChild(img);
slidesDiv.appendChild(section);
Display._slides['0'] = 0;
Display.reinit();
Display.replaceSlides(section);
},
/**
* Set fullscreen image from path
@ -450,10 +506,6 @@ var Display = {
*/
setFullscreenImage: function(bg_color, image) {
Display.clearSlides();
var globalBackground = $("#global-background")[0];
globalBackground.style.cssText = "";
globalBackground.style.setProperty("background", bg_color);
var slidesDiv = $(".slides")[0];
var section = document.createElement("section");
section.setAttribute("id", 0);
section.setAttribute("data-background", bg_color);
@ -462,9 +514,8 @@ var Display = {
img.src = image;
img.setAttribute("style", "height: 100%; width: 100%");
section.appendChild(img);
slidesDiv.appendChild(section);
Display._slides['0'] = 0;
Display.reinit();
Display.replaceSlides(parentSection);
},
/**
* Set fullscreen image from base64 data
@ -473,10 +524,6 @@ var Display = {
*/
setFullscreenImageFromData: function(bg_color, image_data) {
Display.clearSlides();
var globalBackground = $("#global-background")[0];
globalBackground.style.cssText = "";
globalBackground.style.setProperty("background", bg_color);
var slidesDiv = $(".slides")[0];
var section = document.createElement("section");
section.setAttribute("id", 0);
section.setAttribute("data-background", bg_color);
@ -485,7 +532,7 @@ var Display = {
img.src = 'data:image/png;base64,' + image_data;
img.setAttribute("style", "height: 100%; width: 100%");
section.appendChild(img);
slidesDiv.appendChild(section);
Display._slidesContainer.appendChild(section);
Display._slides['0'] = 0;
Display.reinit();
},
@ -632,62 +679,64 @@ var Display = {
}
},
/**
* Add a slide. If the slide exists but the HTML is different, update the slide.
* Create a text 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
* @param {bool} reinit - True if React should reinit when creating a new slide
*/
addTextSlide: function (verse, text, footerText, reinit=true) {
_createTextSlide: function (verse, text) {
var slide;
var html = _prepareText(text);
if (Display._slides.hasOwnProperty(verse)) {
slide = $("#" + verse)[0];
if (slide.innerHTML != html) {
slide.innerHTML = html;
}
} else {
var parent = $("section.text-slides");
if (parent.length === 0) {
Display._createTextContainer();
parent = $("section.text-slides");
}
parent = parent[0];
slide = document.createElement("section");
slide.setAttribute("id", verse);
slide.innerHTML = html;
parent.appendChild(slide);
Display._slides[verse] = parent.children.length - 1;
if (footerText) {
$(".footer")[0].innerHTML = footerText;
}
if (reinit) {
Display.reinit();
}
}
return slide;
},
/**
* Set text slides.
* @param {Object[]} slides - A list of slides to add as JS objects: {"verse": "v1", "text": "line 1\nline2"}
*/
setTextSlides: function (slides) {
Display.clearSlides();
Display._clearSlidesList();
var slide_html;
var parentSection = document.createElement("section");
parentSection.classList = "text-slides";
slides.forEach(function (slide) {
Display.addTextSlide(slide.verse, slide.text, slide.footer, false);
slide_html = Display._createTextSlide(slide.verse, slide.text);
parentSection.appendChild(slide_html);
Display._slides[slide.verse] = parentSection.children.length - 1;
if (slide.footer) {
Display._footerContainer.innerHTML = slide.footer;
}
});
Display.reinit();
Display.replaceSlides(parentSection, true);
},
/**
* Create the <section> that will contain text slides (vertical slides in react)
* Set a single text slide. This changes the slide with no transition.
* Prevents the need to reapply the theme if only changing content.
* @param String slide - Text to put on the slide
*/
_createTextContainer: function () {
var slideContainer = document.createElement("section");
slideContainer.classList = "text-slides";
var slidesDiv = $(".slides")[0];
slidesDiv.appendChild(slideContainer);
// Apply the current theme to the new container
if (!!Display._theme) {
Display.setTheme(Display._theme);
setTextSlide: function (text) {
if (Display._slides.hasOwnProperty("test-slide") && Object.keys(Display._slides).length === 1) {
var slide = $("#" + "test-slide")[0];
var html = _prepareText(text);
if (slide.innerHTML != html) {
slide.innerHTML = html;
}
if (!Display._themeApplied) {
Display.applyTheme(slide.parent);
}
} else {
Display._clearSlidesList();
var slide_html;
var parentSection = document.createElement("section");
parentSection.classList = "text-slides";
slide_html = Display._createTextSlide("test-slide", text);
parentSection.appendChild(slide_html);
Display._slides["test-slide"] = 0;
Display.applyTheme(parentSection);
Display._slidesContainer.innerHTML = "";
Display._slidesContainer.prepend(parentSection);
Display.reinit();
}
},
/**
@ -695,8 +744,7 @@ var Display = {
* @param {Object[]} slides - A list of images to add as JS objects [{"path": "url/to/file"}]
*/
setImageSlides: function (slides) {
Display.clearSlides();
var slidesDiv = $(".slides")[0];
Display._clearSlidesList();
var parentSection = document.createElement("section");
slides.forEach(function (slide, index) {
var section = document.createElement("section");
@ -710,15 +758,14 @@ var Display = {
parentSection.appendChild(section);
Display._slides[index.toString()] = index;
});
slidesDiv.appendChild(parentSection);
Display.reinit();
Display.replaceSlides(parentSection);
},
/**
* Set a video
* @param {Object} video - The video to show as a JS object: {"path": "url/to/file"}
*/
setVideo: function (video) {
Display.clearSlides();
Display._clearSlidesList();
var section = document.createElement("section");
section.setAttribute("data-background", "#000");
var videoElement = document.createElement("video");
@ -747,8 +794,7 @@ var Display = {
mediaWatcher.has_muted(event.target.muted);
});
section.appendChild(videoElement);
$(".slides")[0].appendChild(section);
Display.reinit();
Display.replaceSlides(section);
},
/**
* Play a video
@ -856,32 +902,45 @@ var Display = {
/**
* Blank the screen
*/
blankToBlack: function () {
toBlack: function () {
var documentBody = $("body")[0];
documentBody.style.opacity = 1;
if (!Reveal.isPaused()) {
Reveal.togglePause();
}
// var slidesDiv = $(".slides")[0];
},
/**
* Blank to theme
* Hide all but theme background
*/
blankToTheme: function () {
var slidesDiv = $(".slides")[0];
slidesDiv.style.visibility = "hidden";
var footerDiv = $(".footer")[0];
footerDiv.style.visibility = "hidden";
toTheme: function () {
var documentBody = $("body")[0];
documentBody.style.opacity = 1;
Display._slidesContainer.style.opacity = 0;
Display._footerContainer.style.opacity = 0;
if (Reveal.isPaused()) {
Reveal.togglePause();
}
},
/**
* Hide everything (CAUTION: Causes a invisible mouse barrier)
*/
toTransparent: function () {
Display._slidesContainer.style.opacity = 0;
Display._footerContainer.style.opacity = 0;
var documentBody = $("body")[0];
documentBody.style.opacity = 0;
if (!Reveal.isPaused()) {
Reveal.togglePause();
}
},
/**
* Show the screen
*/
show: function () {
var slidesDiv = $(".slides")[0];
slidesDiv.style.visibility = "visible";
var footerDiv = $(".footer")[0];
footerDiv.style.visibility = "visible";
var documentBody = $("body")[0];
documentBody.style.opacity = 1;
Display._slidesContainer.style.opacity = 1;
Display._footerContainer.style.opacity = 1;
if (Reveal.isPaused()) {
Reveal.togglePause();
}
@ -903,13 +962,31 @@ var Display = {
var dh = parseFloat(_getStyle(d, "height"));
return Math.floor(dh / lh);
},
/**
* Prepare the theme for the next item to be added
* @param theme The theme to be used
*/
setTheme: function (theme) {
if (Display._theme != theme) {
Display._themeApplied = false;
Display._theme = theme;
}
},
/**
* Apply the theme to the provided element
* @param targetElement The target element to apply the theme (expected to be a <section> in the slides container)
* @param is_text Used to decide if the main area constraints should be applied
*/
applyTheme: function (targetElement, is_text=true) {
Display._themeApplied = true;
if (!Display._theme) {
return;
}
// Set slide transitions
var new_transition_type = "none",
new_transition_speed = "default";
if (!!theme.display_slide_transition && Display._doTransitions) {
switch (theme.display_slide_transition_type) {
if (!!Display._theme.display_slide_transition && Display._doTransitions) {
switch (Display._theme.display_slide_transition_type) {
case TransitionType.Fade:
new_transition_type = "fade";
break;
@ -928,7 +1005,7 @@ var Display = {
default:
new_transition_type = "fade";
}
switch (theme.display_slide_transition_speed) {
switch (Display._theme.display_slide_transition_speed) {
case TransitionSpeed.Normal:
new_transition_speed = "default";
break;
@ -941,7 +1018,7 @@ var Display = {
default:
new_transition_speed = "default";
}
switch (theme.display_slide_transition_direction) {
switch (Display._theme.display_slide_transition_direction) {
case TransitionDirection.Vertical:
new_transition_type += "-vertical";
break;
@ -949,95 +1026,102 @@ var Display = {
default:
new_transition_type += "-horizontal";
}
if (theme.display_slide_transition_reverse) {
if (Display._theme.display_slide_transition_reverse) {
new_transition_type += "-reverse";
}
}
Display.setTransition(new_transition_type, new_transition_speed);
var slides = targetElement.children;
for (var i = 0; i < slides.length; i++) {
slides[i].setAttribute("data-transition", new_transition_type);
slides[i].setAttribute("data-transition-speed", new_transition_speed);
}
// Set the background
var globalBackground = $("#global-background")[0];
var backgroundStyle = {};
var backgroundContent = "";
var backgroundHtml = "";
switch (theme.background_type) {
var globalBackground = $("#global-background")[0];
globalBackground.style.setProperty("background", "black");
switch (Display._theme.background_type) {
case BackgroundType.Transparent:
backgroundStyle.background = "transparent";
backgroundContent = "transparent";
globalBackground.style.setProperty("background", "transparent");
break;
case BackgroundType.Solid:
backgroundStyle.background = theme.background_color;
backgroundContent = Display._theme.background_color;
break;
case BackgroundType.Gradient:
switch (theme.background_direction) {
switch (Display._theme.background_direction) {
case GradientType.Horizontal:
backgroundStyle.background = _buildLinearGradient("left top", "left bottom",
theme.background_start_color,
theme.background_end_color);
backgroundContent = _buildLinearGradient("left top", "left bottom",
Display._theme.background_start_color,
Display._theme.background_end_color);
break;
case GradientType.Vertical:
backgroundStyle.background = _buildLinearGradient("left top", "right top",
theme.background_start_color,
theme.background_end_color);
backgroundContent = _buildLinearGradient("left top", "right top",
Display._theme.background_start_color,
Display._theme.background_end_color);
break;
case GradientType.LeftTop:
backgroundStyle.background = _buildLinearGradient("left top", "right bottom",
theme.background_start_color,
theme.background_end_color);
backgroundContent = _buildLinearGradient("left top", "right bottom",
Display._theme.background_start_color,
Display._theme.background_end_color);
break;
case GradientType.LeftBottom:
backgroundStyle.background = _buildLinearGradient("left bottom", "right top",
theme.background_start_color,
theme.background_end_color);
backgroundContent = _buildLinearGradient("left bottom", "right top",
Display._theme.background_start_color,
Display._theme.background_end_color);
break;
case GradientType.Circular:
backgroundStyle.background = _buildRadialGradient(window.innerWidth / 2, theme.background_start_color,
theme.background_end_color);
backgroundContent = _buildRadialGradient(window.innerWidth / 2, Display._theme.background_start_color,
Display._theme.background_end_color);
break;
default:
backgroundStyle.background = "#000";
backgroundContent = "#000";
}
break;
case BackgroundType.Image:
backgroundStyle["background-image"] = "url('" + theme.background_filename + "')";
console.warn(backgroundStyle["background-image"]);
backgroundContent = "url('" + Display._theme.background_filename + "')";
console.warn(backgroundContent);
break;
case BackgroundType.Video:
// never actually used since background type is overridden from video to transparent in window.py
backgroundStyle["background-color"] = theme.background_border_color;
backgroundHtml = "<video loop autoplay muted><source src='" + theme.background_filename + "'></video>";
backgroundContent = Display._theme.background_border_color;
backgroundHtml = "<video loop autoplay muted><source src='" + Display._theme.background_filename + "'></video>";
console.warn(backgroundHtml);
break;
default:
backgroundStyle.background = "#000";
}
globalBackground.style.cssText = "";
for (var bgKey in backgroundStyle) {
if (backgroundStyle.hasOwnProperty(bgKey)) {
globalBackground.style.setProperty(bgKey, backgroundStyle[bgKey]);
}
backgroundContent = "#000";
}
targetElement.style.cssText = "";
targetElement.setAttribute("data-background", backgroundContent);
targetElement.setAttribute("data-background-size", "cover");
if (!!backgroundHtml) {
globalBackground.innerHTML = backgroundHtml;
background.innerHTML = backgroundHtml;
}
// set up the main area
if (!is_text) {
// only transition and background for non text slides
return;
}
mainStyle = {};
if (!!theme.font_main_outline) {
mainStyle["-webkit-text-stroke"] = "" + theme.font_main_outline_size + "pt " +
theme.font_main_outline_color;
mainStyle["-webkit-text-fill-color"] = theme.font_main_color;
if (!!Display._theme.font_main_outline) {
mainStyle["-webkit-text-stroke"] = "" + Display._theme.font_main_outline_size + "pt " +
Display._theme.font_main_outline_color;
mainStyle["-webkit-text-fill-color"] = Display._theme.font_main_color;
}
// These need to be fixed, in the Python they use a width passed in as a parameter
mainStyle.width = theme.font_main_width + "px";
mainStyle.height = theme.font_main_height + "px";
mainStyle["margin-top"] = "" + theme.font_main_y + "px";
mainStyle.left = "" + theme.font_main_x + "px";
mainStyle.color = theme.font_main_color;
mainStyle["font-family"] = theme.font_main_name;
mainStyle["font-size"] = "" + theme.font_main_size + "pt";
mainStyle["font-style"] = !!theme.font_main_italics ? "italic" : "";
mainStyle["font-weight"] = !!theme.font_main_bold ? "bold" : "";
mainStyle["line-height"] = "" + (100 + theme.font_main_line_adjustment) + "%";
mainStyle.width = Display._theme.font_main_width + "px";
mainStyle.height = Display._theme.font_main_height + "px";
mainStyle["margin-top"] = "" + Display._theme.font_main_y + "px";
mainStyle.left = "" + Display._theme.font_main_x + "px";
mainStyle.color = Display._theme.font_main_color;
mainStyle["font-family"] = Display._theme.font_main_name;
mainStyle["font-size"] = "" + Display._theme.font_main_size + "pt";
mainStyle["font-style"] = !!Display._theme.font_main_italics ? "italic" : "";
mainStyle["font-weight"] = !!Display._theme.font_main_bold ? "bold" : "";
mainStyle["line-height"] = "" + (100 + Display._theme.font_main_line_adjustment) + "%";
// Using text-align-last because there is a <br> seperating each line
switch (theme.display_horizontal_align) {
switch (Display._theme.display_horizontal_align) {
case HorizontalAlign.Justify:
mainStyle["text-align"] = "justify";
mainStyle["text-align-last"] = "justify";
@ -1058,7 +1142,7 @@ var Display = {
mainStyle["text-align"] = "center";
mainStyle["text-align-last"] = "center";
}
switch (theme.display_vertical_align) {
switch (Display._theme.display_vertical_align) {
case VerticalAlign.Middle:
mainStyle["justify-content"] = "center";
break;
@ -1068,23 +1152,19 @@ var Display = {
case VerticalAlign.Bottom:
mainStyle["justify-content"] = "flex-end";
// This gets around the webkit scroll height bug
mainStyle["padding-bottom"] = "" + (theme.font_main_size / 8) + "px";
mainStyle["padding-bottom"] = "" + (Display._theme.font_main_size / 8) + "px";
break;
default:
mainStyle["justify-content"] = "center";
}
if (theme.hasOwnProperty('font_main_shadow_size') && !!theme.font_main_shadow) {
mainStyle["text-shadow"] = theme.font_main_shadow_color + " " + theme.font_main_shadow_size + "pt " +
theme.font_main_shadow_size + "pt";
if (Display._theme.hasOwnProperty('font_main_shadow_size') && !!Display._theme.font_main_shadow) {
mainStyle["text-shadow"] = Display._theme.font_main_shadow_color + " " + Display._theme.font_main_shadow_size + "pt " +
Display._theme.font_main_shadow_size + "pt";
}
var slidesDiv = $("section.text-slides");
if (slidesDiv.length > 0) {
slidesDiv = slidesDiv[0];
slidesDiv.style.cssText = "";
targetElement.style.cssText = "";
for (var mainKey in mainStyle) {
if (mainStyle.hasOwnProperty(mainKey)) {
slidesDiv.style.setProperty(mainKey, mainStyle[mainKey]);
}
targetElement.style.setProperty(mainKey, mainStyle[mainKey]);
}
}
// Set up the footer
@ -1092,19 +1172,18 @@ var Display = {
"text-align": "left"
};
footerStyle.position = "absolute";
footerStyle.left = "" + theme.font_footer_x + "px";
footerStyle.top = "" + theme.font_footer_y + "px";
footerStyle.width = "" + theme.font_footer_width + "px";
footerStyle.height = "" + theme.font_footer_height + "px";
footerStyle.color = theme.font_footer_color;
footerStyle["font-family"] = theme.font_footer_name;
footerStyle["font-size"] = "" + theme.font_footer_size + "pt";
footerStyle["white-space"] = theme.font_footer_wrap ? "normal" : "nowrap";
var footer = $(".footer")[0];
footer.style.cssText = "";
footerStyle.left = "" + Display._theme.font_footer_x + "px";
footerStyle.top = "" + Display._theme.font_footer_y + "px";
footerStyle.width = "" + Display._theme.font_footer_width + "px";
footerStyle.height = "" + Display._theme.font_footer_height + "px";
footerStyle.color = Display._theme.font_footer_color;
footerStyle["font-family"] = Display._theme.font_footer_name;
footerStyle["font-size"] = "" + Display._theme.font_footer_size + "pt";
footerStyle["white-space"] = Display._theme.font_footer_wrap ? "normal" : "nowrap";
Display._footerContainer.style.cssText = "";
for (var footerKey in footerStyle) {
if (footerStyle.hasOwnProperty(footerKey)) {
footer.style.setProperty(footerKey, footerStyle[footerKey]);
Display._footerContainer.style.setProperty(footerKey, footerStyle[footerKey]);
}
}
},

View File

@ -812,7 +812,7 @@ class ThemePreviewRenderer(LogMixin, DisplayWindow):
"""
self.clear_slides()
self.log_debug('_text_fits_on_slide: 1\n{text}'.format(text=text))
self.run_javascript('Display.addTextSlide("v1", "{text}", "Dummy Footer");'
self.run_javascript('Display.setTextSlide("{text}");'
.format(text=text.replace('"', '\\"')), is_sync=True)
self.log_debug('_text_fits_on_slide: 2')
does_text_fits = self.run_javascript('Display.doesContentFit();', is_sync=True)

View File

@ -244,7 +244,9 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties):
Add stuff after page initialisation
"""
js_is_display = str(self.is_display).lower()
self.run_javascript('Display.init({do_transitions});'.format(do_transitions=js_is_display))
item_transitions = str(self.settings.value('themes/item transitions')).lower()
self.run_javascript('Display.init({do_transitions}, {do_item_transitions});'
.format(do_transitions=js_is_display, do_item_transitions=item_transitions))
wait_for(lambda: self._is_initialised)
if self.scale != 1:
self.set_scale(self.scale)
@ -430,12 +432,6 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties):
if self.is_display:
Registry().execute('live_display_active')
def blank_to_theme(self):
"""
Blank to theme
"""
self.run_javascript('Display.blankToTheme();')
def hide_display(self, mode=HideMode.Screen):
"""
Hide the display by making all layers transparent Store the images so they can be replaced when required
@ -450,9 +446,11 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties):
if mode == HideMode.Screen:
self.setVisible(False)
elif mode == HideMode.Blank:
self.run_javascript('Display.blankToBlack();')
self.run_javascript('Display.toBlack();')
elif mode == HideMode.Theme:
self.run_javascript('Display.toTheme();')
else:
self.run_javascript('Display.blankToTheme();')
self.run_javascript('Display.toTransparent();')
if mode != HideMode.Screen:
if self.isHidden():
self.setVisible(True)

View File

@ -41,6 +41,7 @@ class HideMode(object):
Blank = 1
Theme = 2
Screen = 3
Transparent = 4
class DisplayControllerType(object):

View File

@ -40,7 +40,7 @@ from openlp.core.common.path import path_to_str
from openlp.core.common.registry import Registry, RegistryBase
from openlp.core.lib.serviceitem import ItemCapabilities
from openlp.core.lib.ui import critical_error_message_box
from openlp.core.ui import DisplayControllerType
from openlp.core.ui import DisplayControllerType, HideMode
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path, parse_devicestream_path, \
VIDEO_EXT, AUDIO_EXT
from openlp.core.ui.media.remote import register_views
@ -454,7 +454,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.media_info.is_playing = True
if not controller.media_info.is_background:
display = self._define_display(controller)
display.setVisible(False)
display.hide_display(HideMode.Transparent)
controller._set_theme(controller.service_item)
display.load_verses([{"verse": "v1", "text": "", "footer": " "}])
return True
def tick(self, controller):
@ -466,9 +468,14 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
start_again = False
if controller.media_info.is_playing and controller.media_info.length > 0:
if controller.media_info.timer > controller.media_info.length:
self.media_stop(controller)
if controller.media_info.is_looping_playback:
start_again = True
else:
self.media_stop(controller)
elif controller.media_info.timer > controller.media_info.length - TICK_TIME * 4:
if not controller.media_info.is_looping_playback:
display = self._define_display(controller)
display.show_display()
controller.media_info.timer += TICK_TIME
seconds = controller.media_info.timer // 1000
minutes = seconds // 60
@ -479,7 +486,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
(minutes, seconds, total_minutes, total_seconds))
if start_again:
self.media_play(controller, True)
controller.seek_slider.setSliderPosition(0)
self.media_play(controller, False)
def media_pause_msg(self, msg):
"""
@ -560,6 +568,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
controller.media_info.is_playing = False
controller.media_info.timer = 1000
controller.media_timer = 0
display = self._define_display(controller)
display.show_display()
def media_volume_msg(self, msg):
"""
@ -615,6 +625,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
"""
self.set_controls_visible(controller, False)
if controller.controller_type in self.current_media_players:
display = self._define_display(controller)
display.show_display()
self.current_media_players[controller.controller_type].reset(controller)
self.current_media_players[controller.controller_type].set_visible(controller, False)
del self.current_media_players[controller.controller_type]

View File

@ -855,7 +855,7 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
self.preview_display.set_theme(theme_data, service_item_type=service_item.service_item_type)
# Set theme for displays
for display in self.displays:
display.set_theme(service_item.get_theme_data(), service_item_type=service_item.service_item_type)
display.set_theme(theme_data, service_item_type=service_item.service_item_type)
def _process_item(self, service_item, slide_no):
"""
@ -1168,20 +1168,9 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
Registry().execute('{text}_slide'.format(text=self.service_item.name.lower()),
[self.service_item, self.is_live, row])
else:
# to_display = self.service_item.get_rendered_frame(row)
if self.service_item.is_text():
for display in self.displays:
display.go_to_slide(row)
# self.display.text(to_display, row != old_selected_row)
else:
if start:
for display in self.displays:
display.load_images(self.service_item.slides)
# self.display.build_html(self.service_item, to_display)
else:
for display in self.displays:
display.go_to_slide(row)
# self.display.image(to_display)
if not self.service_item.is_text():
# reset the store used to display first image
self.service_item.bg_image_bytes = None
self.selected_row = row

View File

@ -69,6 +69,9 @@ class ThemesTab(SettingsTab):
self.wrap_footer_check_box = QtWidgets.QCheckBox(self.universal_group_box)
self.wrap_footer_check_box.setObjectName('wrap_footer_check_box')
self.universal_group_box_layout.addWidget(self.wrap_footer_check_box)
self.item_transitions_check_box = QtWidgets.QCheckBox(self.universal_group_box)
self.item_transitions_check_box.setObjectName('item_transitions_check_box')
self.universal_group_box_layout.addWidget(self.item_transitions_check_box)
self.left_layout.addWidget(self.universal_group_box)
self.left_layout.addStretch()
self.level_group_box = QtWidgets.QGroupBox(self.right_column)
@ -115,6 +118,7 @@ class ThemesTab(SettingsTab):
self.global_group_box.setTitle(translate('OpenLP.ThemesTab', 'Global Theme'))
self.universal_group_box.setTitle(translate('OpenLP.ThemesTab', 'Universal Settings'))
self.wrap_footer_check_box.setText(translate('OpenLP.ThemesTab', '&Wrap footer text'))
self.item_transitions_check_box.setText(translate('OpenLP.ThemesTab', '&Transition between service items'))
self.level_group_box.setTitle(translate('OpenLP.ThemesTab', 'Theme Level'))
self.song_level_radio_button.setText(translate('OpenLP.ThemesTab', 'S&ong Level'))
self.song_level_label.setText(
@ -139,6 +143,7 @@ class ThemesTab(SettingsTab):
self.theme_level = self.settings.value('theme level')
self.global_theme = self.settings.value('global theme')
self.wrap_footer_check_box.setChecked(self.settings.value('wrap footer'))
self.item_transitions_check_box.setChecked(self.settings.value('item transitions'))
self.settings.endGroup()
if self.theme_level == ThemeLevel.Global:
self.global_level_radio_button.setChecked(True)
@ -155,6 +160,7 @@ class ThemesTab(SettingsTab):
self.settings.setValue('theme level', self.theme_level)
self.settings.setValue('global theme', self.global_theme)
self.settings.setValue('wrap footer', self.wrap_footer_check_box.isChecked())
self.settings.setValue('item transitions', self.item_transitions_check_box.isChecked())
self.settings.endGroup()
self.renderer.set_theme_level(self.theme_level)
if self.tab_visited:

View File

@ -32,6 +32,7 @@ from PyQt5 import QtCore
sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
from openlp.core.display.window import DisplayWindow
from openlp.core.ui import HideMode
@patch('PyQt5.QtWidgets.QVBoxLayout')
@ -103,6 +104,31 @@ def test_set_scale_initialised(mocked_webengine, mocked_addWidget, mock_settings
display_window.run_javascript.assert_called_once_with('Display.setScale(50.0);')
@patch('PyQt5.QtWidgets.QVBoxLayout')
@patch('openlp.core.display.webengine.WebEngineView')
def test_after_loaded(mocked_webengine, mocked_addWidget, mock_settings):
"""
Test the correct steps are taken when the webview is loaded
"""
# GIVEN: An initialised display window and settings for item transitions returns true
display_window = DisplayWindow()
display_window.is_display = True
mock_settings.value.return_value = True
display_window.scale = 2
display_window._is_initialised = True
display_window.run_javascript = MagicMock()
display_window.set_scale = MagicMock()
display_window.set_startup_screen = MagicMock()
# WHEN: after_loaded is run
display_window.after_loaded()
# THEN: The following functions should have been called
display_window.run_javascript.assert_called_once_with('Display.init(true, true);')
display_window.set_scale.assert_called_once_with(2)
display_window.set_startup_screen.assert_called_once()
@patch('PyQt5.QtWidgets.QVBoxLayout')
@patch('openlp.core.display.webengine.WebEngineView')
@patch.object(time, 'time')
@ -146,3 +172,75 @@ def test_run_javascript_sync_no_wait(mock_time, mocked_webengine, mocked_addWidg
assert result == 1234
webengine_page.runJavaScript.assert_called_once()
mock_time.sleep.assert_not_called()
@patch('PyQt5.QtWidgets.QVBoxLayout')
@patch('openlp.core.display.webengine.WebEngineView')
def test_hide_display_to_screen(mocked_webengine, mocked_addWidget, mock_settings):
"""
Test hide to screen in the hide_display function
"""
# GIVEN:
display_window = DisplayWindow()
display_window.setVisible = MagicMock()
# WHEN: Hide display is run with no mode (should default to Screen)
display_window.hide_display()
# THEN: Should hide the display and set the hide mode
display_window.setVisible.assert_called_once_with(False)
assert display_window.hide_mode == HideMode.Screen
@patch('PyQt5.QtWidgets.QVBoxLayout')
@patch('openlp.core.display.webengine.WebEngineView')
def test_hide_display_to_blank(mocked_webengine, mocked_addWidget, mock_settings):
"""
Test hide to screen in the hide_display function
"""
# GIVEN:
display_window = DisplayWindow()
display_window.run_javascript = MagicMock()
# WHEN: Hide display is run with HideMode.Blank
display_window.hide_display(HideMode.Blank)
# THEN: Should run the correct javascript on the display and set the hide mode
display_window.run_javascript.assert_called_once_with('Display.toBlack();')
assert display_window.hide_mode == HideMode.Blank
@patch('PyQt5.QtWidgets.QVBoxLayout')
@patch('openlp.core.display.webengine.WebEngineView')
def test_hide_display_to_theme(mocked_webengine, mocked_addWidget, mock_settings):
"""
Test hide to screen in the hide_display function
"""
# GIVEN:
display_window = DisplayWindow()
display_window.run_javascript = MagicMock()
# WHEN: Hide display is run with HideMode.Theme
display_window.hide_display(HideMode.Theme)
# THEN: Should run the correct javascript on the display and set the hide mode
display_window.run_javascript.assert_called_once_with('Display.toTheme();')
assert display_window.hide_mode == HideMode.Theme
@patch('PyQt5.QtWidgets.QVBoxLayout')
@patch('openlp.core.display.webengine.WebEngineView')
def test_hide_display_to_transparent(mocked_webengine, mocked_addWidget, mock_settings):
"""
Test hide to screen in the hide_display function
"""
# GIVEN:
display_window = DisplayWindow()
display_window.run_javascript = MagicMock()
# WHEN: Hide display is run with HideMode.Transparent
display_window.hide_display(HideMode.Transparent)
# THEN: Should run the correct javascript on the display and set the hide mode
display_window.run_javascript.assert_called_once_with('Display.toTransparent();')
assert display_window.hide_mode == HideMode.Transparent

View File

@ -324,3 +324,23 @@ def test_setup_display(MockItemMediaInfo, media_env):
assert controller.has_audio is False
media_env.media_controller._define_display.assert_called_once_with(controller)
media_env.media_controller.vlc_player.setup(controller, mocked_display, False)
def test_media_play(media_env):
"""
Test that the display/controllers are set up correctly
"""
# GIVEN: A mocked controller where is_background is false
media_env.current_media_players = MagicMock()
Registry().register('settings', MagicMock())
media_env.live_timer = MagicMock()
mocked_controller = MagicMock()
mocked_controller.media_info.is_background = False
# WHEN: media_play is called
result = media_env.media_play(mocked_controller)
# THEN: The web display should become transparent (only tests that the theme is reset here)
# And the function should return true to indicate success
assert result is True
mocked_controller._set_theme.assert_called_once()

View File

@ -43,6 +43,29 @@ def test_initial_slide_controller(registry):
assert slide_controller.is_live is False, 'The base slide controller should not be a live controller'
def test_slide_selected(settings):
"""
Test the slide selected method
"""
# GIVEN: A new SlideController instance on slide 4 of 10, and is not a command
slide_controller = SlideController(None)
slide_controller.update_preview = MagicMock()
slide_controller.slide_selected_lock = MagicMock()
slide_controller.service_item = MagicMock()
slide_controller.service_item.is_command.return_value = False
slide_controller.preview_widget = MagicMock()
slide_controller.preview_widget.slide_count.return_value = 10
slide_controller.preview_widget.current_slide_number.return_value = 4
mocked_display = MagicMock()
slide_controller.displays = [mocked_display]
# WHEN: The slide_selected method is run
slide_controller.slide_selected(True)
# THEN: The display is updated with the slide number
mocked_display.go_to_slide.assert_called_once_with(4)
def test_text_service_item_blank(settings):
"""
Test that loading a text-based service item into the slide controller sets the correct blank menu

View File

@ -76,6 +76,7 @@ describe("The function", function () {
});
describe("The Display object", function () {
it("should start with a blank _slides object", function () {
expect(Display._slides).toEqual({});
});
@ -107,6 +108,8 @@ describe("The Display object", function () {
it("should initialise Reveal when init is called", function () {
spyOn(Reveal, "initialize");
document.body.innerHTML = "";
_createDiv({"id": "global-background"});
Display.init();
expect(Reveal.initialize).toHaveBeenCalled();
});
@ -123,16 +126,18 @@ describe("The Display object", function () {
expect(Reveal.slide).toHaveBeenCalledWith(0);
});
it("should have a setTransition() method", function () {
expect(Display.setTransition).toBeDefined();
it("should have a setItemTransition() method", function () {
expect(Display.setItemTransition).toBeDefined();
});
it("should have a correctly functioning clearSlides() method", function () {
expect(Display.clearSlides).toBeDefined();
document.body.innerHTML = "";
var slidesDiv = _createDiv({"class": "slides"});
slidesDiv.innerHTML = "<section><p></p></section>";
Display._slidesContainer = slidesDiv;
var footerDiv = _createDiv({"class": "footer"});
Display._footerContainer = footerDiv;
Display.clearSlides();
expect($(".slides")[0].innerHTML).toEqual("");
@ -167,42 +172,48 @@ describe("Transitions", function () {
Display._theme = null;
});
it("should have a correctly functioning setTransition() method", function () {
it("should have a correctly functioning setItemTransition() method", function () {
spyOn(Reveal, "configure");
Display.setTransition("fade", "slow");
expect(Reveal.configure).toHaveBeenCalledWith({"transition": "fade", "transitionSpeed": "slow"});
Display.setItemTransition(true);
expect(Reveal.configure).toHaveBeenCalledWith({"backgroundTransition": "fade", "transitionSpeed": "default"});
});
it("should have enabled transitions when _doTransitions is true and setTheme is run", function () {
spyOn(Display, "setTransition");
it("should have enabled transitions when _doTransitions is true and applyTheme is run", function () {
Display._doTransitions = true;
var theme = {
"display_slide_transition": true,
"display_slide_transition_type": TransitionType.Slide,
"display_slide_transition_speed": TransitionSpeed.Fast
}
Display.setTheme(theme);
var slidesDiv = _createDiv({"class": "slides"});
slidesDiv.innerHTML = "<section><section><p></p></section></section>";
Display._slidesContainer = slidesDiv;
expect(Display.setTransition).toHaveBeenCalledWith("slide-horizontal", "fast");
Display.applyTheme(Display._slidesContainer.children[0])
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition")).toEqual("slide-horizontal");
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition-speed")).toEqual("fast");
});
it("should have not enabled transitions when init() with no transitions and setTheme is run", function () {
spyOn(Display, "setTransition");
Display._doTransitions = false;
var theme = {
"display_slide_transition": true,
"display_slide_transition_type": TransitionType.Slide,
"display_slide_transition_speed": TransitionSpeed.Fast,
}
Display.setTheme(theme);
var slidesDiv = _createDiv({"class": "slides"});
slidesDiv.innerHTML = "<section><section><p></p></section></section>";
Display._slidesContainer = slidesDiv;
expect(Display.setTransition).toHaveBeenCalledWith("none", "default");
Display.applyTheme(Display._slidesContainer.children[0])
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition")).toEqual("none");
});
it("should have enabled transitions in the correct direction", function () {
spyOn(Display, "setTransition");
Display._doTransitions = true;
var theme = {
"display_slide_transition": true,
@ -211,10 +222,15 @@ describe("Transitions", function () {
"display_slide_transition_direction": TransitionDirection.Vertical,
"display_slide_transition_reverse": true,
}
Display.setTheme(theme);
var slidesDiv = _createDiv({"class": "slides"});
slidesDiv.innerHTML = "<section><section><p></p></section></section>";
Display._slidesContainer = slidesDiv;
expect(Display.setTransition).toHaveBeenCalledWith("convex-vertical-reverse", "slow");
Display.applyTheme(Display._slidesContainer.children[0])
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition")).toEqual("convex-vertical-reverse");
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition-speed")).toEqual("slow");
});
});
@ -504,63 +520,49 @@ describe("Display.alertAnimationEndEvent", function () {
});
});
describe("Display.addTextSlide", function () {
describe("Display.setTextSlide", function () {
beforeEach(function() {
document.body.innerHTML = "";
_createDiv({"class": "slides"});
_createDiv({"class": "footer"});
var slides_container = _createDiv({"class": "slides"});
var footer_container = _createDiv({"class": "footer"});
Display._slidesContainer = slides_container;
Display._footerContainer = footer_container;
Display._slides = {};
});
it("should add a new slide", function () {
var verse = "v1",
text = "Amazing grace,\nhow sweet the sound",
footer = "Public Domain";
var text = "Amazing grace,\nhow sweet the sound";
spyOn(Display, "reinit");
Display.addTextSlide(verse, text, footer);
Display.setTextSlide(text);
expect(Display._slides[verse]).toEqual(0);
expect(Display._slides["test-slide"]).toEqual(0);
expect($(".slides > section > section").length).toEqual(1);
expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
expect(Display.reinit).toHaveBeenCalled();
});
it("should add a new slide without calling reinit()", function () {
var verse = "v1",
text = "Amazing grace,\nhow sweet the sound",
footer = "Public Domain";
spyOn(Display, "reinit");
Display.addTextSlide(verse, text, footer, false);
expect(Display._slides[verse]).toEqual(0);
expect($(".slides > section > section").length).toEqual(1);
expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
expect(Display.reinit).not.toHaveBeenCalled();
});
it("should update an existing slide", function () {
var verse = "v1",
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);
var text = "That saved a wretch\nlike me";
spyOn(Display, "reinit");
Display.setTextSlide("Amazing grace,\nhow sweet the sound");
Display.addTextSlide(verse, text, footer, true);
Display.setTextSlide(text);
expect(Display._slides[verse]).toEqual(0);
expect(Display._slides["test-slide"]).toEqual(0);
expect($(".slides > section > section").length).toEqual(1);
expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
expect(Display.reinit).toHaveBeenCalledTimes(0);
expect(Display.reinit).toHaveBeenCalledTimes(1); // only called once for the first setTextSlide
});
});
describe("Display.setTextSlides", function () {
beforeEach(function() {
document.body.innerHTML = "";
_createDiv({"class": "slides"});
_createDiv({"class": "footer"});
var slides_container = _createDiv({"class": "slides"});
var footer_container = _createDiv({"class": "footer"});
Display._slidesContainer = slides_container;
Display._footerContainer = footer_container;
_createDiv({"id": "global-background"});
Display._slides = {};
});
@ -581,15 +583,16 @@ describe("Display.setTextSlides", function () {
}
];
spyOn(Display, "clearSlides");
spyOn(Display, "reinit");
spyOn(Reveal, "sync");
spyOn(Reveal, "slide");
Display.setTextSlides(slides);
expect(Display.clearSlides).toHaveBeenCalledTimes(1);
expect(Display.clearSlides).toHaveBeenCalledTimes(0);
expect(Display._slides["v1"]).toEqual(0);
expect(Display._slides["v2"]).toEqual(1);
expect($(".slides > section > section").length).toEqual(2);
expect(Display.reinit).toHaveBeenCalledTimes(1);
expect(Reveal.sync).toHaveBeenCalledTimes(1);
});
it("should correctly set outline width", function () {
@ -607,7 +610,7 @@ describe("Display.setTextSlides", function () {
'font_main_outline_size': 42,
'font_main_outline_color': 'red'
};
spyOn(Display, "reinit");
spyOn(Reveal, "sync");
spyOn(Reveal, "slide");
Display.setTheme(theme);
@ -633,7 +636,7 @@ describe("Display.setTextSlides", function () {
'display_horizontal_align': 3,
'display_vertical_align': 1
};
spyOn(Display, "reinit");
spyOn(Reveal, "sync");
spyOn(Reveal, "slide");
Display.setTheme(theme);
@ -659,7 +662,7 @@ describe("Display.setTextSlides", function () {
'font_main_shadow_color': "#000",
'font_main_shadow_size': 5
};
spyOn(Display, "reinit");
spyOn(Reveal, "sync");
spyOn(Reveal, "slide");
Display.setTheme(theme);
@ -684,7 +687,7 @@ describe("Display.setTextSlides", function () {
'font_main_shadow_color': "#000",
'font_main_shadow_size': 5
};
spyOn(Display, "reinit");
spyOn(Reveal, "sync");
spyOn(Reveal, "slide");
Display.setTheme(theme);
@ -710,7 +713,7 @@ describe("Display.setTextSlides", function () {
'font_main_width': 1230,
'font_main_height': 4560
};
spyOn(Display, "reinit");
spyOn(Reveal, "sync");
spyOn(Reveal, "slide");
Display.setTheme(theme);
@ -727,20 +730,21 @@ describe("Display.setTextSlides", function () {
describe("Display.setImageSlides", function () {
beforeEach(function() {
document.body.innerHTML = "";
_createDiv({"class": "slides"});
_createDiv({"class": "footer"});
var slides_container = _createDiv({"class": "slides"});
var footer_container = _createDiv({"class": "footer"});
Display._slidesContainer = slides_container;
Display._footerContainer = footer_container;
_createDiv({"id": "global-background"});
Display._slides = {};
});
it("should add a list of images", function () {
var slides = [{"path": "file:///openlp1.jpg"}, {"path": "file:///openlp2.jpg"}];
spyOn(Display, "clearSlides");
spyOn(Display, "reinit");
spyOn(Reveal, "sync");
spyOn(Reveal, "slide");
Display.setImageSlides(slides);
expect(Display.clearSlides).toHaveBeenCalledTimes(1);
expect(Display._slides["0"]).toEqual(0);
expect(Display._slides["1"]).toEqual(1);
expect($(".slides > section > section").length).toEqual(2);
@ -749,30 +753,30 @@ describe("Display.setImageSlides", function () {
expect($(".slides > section > section > img")[0].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);")
expect($(".slides > section > section > img")[1].getAttribute("src")).toEqual("file:///openlp2.jpg")
expect($(".slides > section > section > img")[1].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);")
expect(Display.reinit).toHaveBeenCalledTimes(1);
expect(Reveal.sync).toHaveBeenCalledTimes(1);
});
});
describe("Display.setVideo", function () {
beforeEach(function() {
document.body.innerHTML = "";
_createDiv({"class": "slides"});
var slides_container = _createDiv({"class": "slides"});
Display._slidesContainer = slides_container;
_createDiv({"id": "global-background"});
Display._slides = {};
});
it("should add a video to the page", function () {
var video = {"path": "file:///video.mp4"};
spyOn(Display, "clearSlides");
spyOn(Display, "reinit");
spyOn(Reveal, "sync");
spyOn(Reveal, "slide");
Display.setVideo(video);
expect(Display.clearSlides).toHaveBeenCalledTimes(1);
expect($(".slides > section").length).toEqual(1);
expect($(".slides > section > video").length).toEqual(1);
expect($(".slides > section > video")[0].src).toEqual("file:///video.mp4")
expect(Display.reinit).toHaveBeenCalledTimes(1);
expect(Reveal.sync).toHaveBeenCalledTimes(1);
});
});