diff --git a/openlp/core/common/settings.py b/openlp/core/common/settings.py
index 545eebed0..63dd0399b 100644
--- a/openlp/core/common/settings.py
+++ b/openlp/core/common/settings.py
@@ -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,
diff --git a/openlp/core/display/html/display.css b/openlp/core/display/html/display.css
index 2f37f6323..518594686 100644
--- a/openlp/core/display/html/display.css
+++ b/openlp/core/display/html/display.css
@@ -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 {
diff --git a/openlp/core/display/html/display.js b/openlp/core/display/html/display.js
index 67ef7355d..2469f75b8 100644
--- a/openlp/core/display/html/display.js
+++ b/openlp/core/display/html/display.js
@@ -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];
}
- 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
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();
- }
- }
+ slide = document.createElement("section");
+ slide.setAttribute("id", verse);
+ slide.innerHTML = html;
+ 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 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) {
- Display._theme = 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 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 = "";
+ backgroundContent = Display._theme.background_border_color;
+ backgroundHtml = "";
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
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 = "";
- for (var mainKey in mainStyle) {
- if (mainStyle.hasOwnProperty(mainKey)) {
- slidesDiv.style.setProperty(mainKey, mainStyle[mainKey]);
- }
+ targetElement.style.cssText = "";
+ for (var mainKey in mainStyle) {
+ if (mainStyle.hasOwnProperty(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]);
}
}
},
diff --git a/openlp/core/display/render.py b/openlp/core/display/render.py
index 3b4fe564b..12205d042 100644
--- a/openlp/core/display/render.py
+++ b/openlp/core/display/render.py
@@ -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)
diff --git a/openlp/core/display/window.py b/openlp/core/display/window.py
index fc64f475c..8cc16fd96 100644
--- a/openlp/core/display/window.py
+++ b/openlp/core/display/window.py
@@ -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)
diff --git a/openlp/core/ui/__init__.py b/openlp/core/ui/__init__.py
index 1b159286c..9dbdc836e 100644
--- a/openlp/core/ui/__init__.py
+++ b/openlp/core/ui/__init__.py
@@ -41,6 +41,7 @@ class HideMode(object):
Blank = 1
Theme = 2
Screen = 3
+ Transparent = 4
class DisplayControllerType(object):
diff --git a/openlp/core/ui/media/mediacontroller.py b/openlp/core/ui/media/mediacontroller.py
index 305e1ea21..3383f8c5c 100644
--- a/openlp/core/ui/media/mediacontroller.py
+++ b/openlp/core/ui/media/mediacontroller.py
@@ -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]
diff --git a/openlp/core/ui/slidecontroller.py b/openlp/core/ui/slidecontroller.py
index 13eb9ebb7..730aee2c6 100644
--- a/openlp/core/ui/slidecontroller.py
+++ b/openlp/core/ui/slidecontroller.py
@@ -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)
+ for display in self.displays:
+ display.go_to_slide(row)
+ 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
diff --git a/openlp/core/ui/themestab.py b/openlp/core/ui/themestab.py
index 7326c9d04..5cca1509e 100644
--- a/openlp/core/ui/themestab.py
+++ b/openlp/core/ui/themestab.py
@@ -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:
diff --git a/tests/functional/openlp_core/display/test_window.py b/tests/functional/openlp_core/display/test_window.py
index f9e78aaf5..d3bff5b72 100644
--- a/tests/functional/openlp_core/display/test_window.py
+++ b/tests/functional/openlp_core/display/test_window.py
@@ -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
diff --git a/tests/functional/openlp_core/ui/media/test_mediacontroller.py b/tests/functional/openlp_core/ui/media/test_mediacontroller.py
index 169d29198..7fef46827 100644
--- a/tests/functional/openlp_core/ui/media/test_mediacontroller.py
+++ b/tests/functional/openlp_core/ui/media/test_mediacontroller.py
@@ -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()
diff --git a/tests/functional/openlp_core/ui/test_slidecontroller.py b/tests/functional/openlp_core/ui/test_slidecontroller.py
index 9a07bbfd4..a1fd34703 100644
--- a/tests/functional/openlp_core/ui/test_slidecontroller.py
+++ b/tests/functional/openlp_core/ui/test_slidecontroller.py
@@ -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
diff --git a/tests/js/test_display.js b/tests/js/test_display.js
index 1fb7aaaae..acaf180b9 100644
--- a/tests/js/test_display.js
+++ b/tests/js/test_display.js
@@ -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 = "";
+ 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 = "";
+ 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 = "";
+ 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 = "";
+ 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);
});
});