forked from openlp/openlp
Merge branch 'between-item-transitions' into 'master'
Transitions between items Closes #191 See merge request openlp/openlp!121
This commit is contained in:
commit
b51829d4d6
@ -333,6 +333,7 @@ class Settings(QtCore.QSettings):
|
|||||||
'themes/last directory import': None,
|
'themes/last directory import': None,
|
||||||
'themes/theme level': ThemeLevel.Global,
|
'themes/theme level': ThemeLevel.Global,
|
||||||
'themes/wrap footer': False,
|
'themes/wrap footer': False,
|
||||||
|
'themes/item transitions': False,
|
||||||
'user interface/live panel': True,
|
'user interface/live panel': True,
|
||||||
'user interface/live splitter geometry': QtCore.QByteArray(),
|
'user interface/live splitter geometry': QtCore.QByteArray(),
|
||||||
'user interface/lock panel': True,
|
'user interface/lock panel': True,
|
||||||
|
@ -22,14 +22,53 @@ sup {
|
|||||||
font-size: smaller !important;
|
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,
|
||||||
.reveal .slides > section > section {
|
.reveal .slides > section > section {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
max-height: 100%;
|
max-height: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
.reveal .slides > section.text-slides {
|
.reveal .slides > section {
|
||||||
/* Need to override reveal styles to use our text aligment */
|
/* Need to override reveal styles to use our text alignment */
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
word-wrap: break-word;
|
word-wrap: break-word;
|
||||||
@ -318,21 +357,24 @@ sup {
|
|||||||
.reveal[class*=fade] .slides section.present:not([data-transition]) {
|
.reveal[class*=fade] .slides section.present:not([data-transition]) {
|
||||||
transition-delay: 400ms; }
|
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]) {
|
.reveal[data-transition-speed="fast"][class*=fade] .slides section.present:not([data-transition]) {
|
||||||
transition-delay: 200ms; }
|
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]) {
|
.reveal[data-transition-speed="slow"][class*=fade] .slides section.present:not([data-transition]) {
|
||||||
transition-delay: 800ms; }
|
transition-delay: 800ms; }
|
||||||
|
|
||||||
.reveal[class*=fade] .slides section {
|
.reveal[class*=fade] .slides section,
|
||||||
|
.reveal .slides section[class*=fade] {
|
||||||
transition-duration: 400ms !important; }
|
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; }
|
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; }
|
transition-duration: 800ms !important; }
|
||||||
|
|
||||||
.reveal[class*=fade].overview .slides section {
|
.reveal[class*=fade].overview .slides section {
|
||||||
|
@ -352,6 +352,9 @@ AudioPlayer.prototype.stop = function () {
|
|||||||
* The Display object is what we use from OpenLP
|
* The Display object is what we use from OpenLP
|
||||||
*/
|
*/
|
||||||
var Display = {
|
var Display = {
|
||||||
|
_slidesContainer: null,
|
||||||
|
_footerContainer: null,
|
||||||
|
_backgroundsContainer: null,
|
||||||
_alerts: [],
|
_alerts: [],
|
||||||
_slides: {},
|
_slides: {},
|
||||||
_alertSettings: {},
|
_alertSettings: {},
|
||||||
@ -359,6 +362,8 @@ var Display = {
|
|||||||
_transitionState: TransitionState.NoTransition,
|
_transitionState: TransitionState.NoTransition,
|
||||||
_animationState: AnimationState.NoAnimation,
|
_animationState: AnimationState.NoAnimation,
|
||||||
_doTransitions: false,
|
_doTransitions: false,
|
||||||
|
_doItemTransitions: false,
|
||||||
|
_themeApplied: true,
|
||||||
_revealConfig: {
|
_revealConfig: {
|
||||||
margin: 0.0,
|
margin: 0.0,
|
||||||
minScale: 1.0,
|
minScale: 1.0,
|
||||||
@ -380,9 +385,16 @@ var Display = {
|
|||||||
/**
|
/**
|
||||||
* Start up reveal and do any other initialisation
|
* 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;
|
Display._doTransitions = doTransitions;
|
||||||
Reveal.initialize(Display._revealConfig);
|
Reveal.initialize(Display._revealConfig);
|
||||||
|
Display.setItemTransition(doItemtransitions && doTransitions);
|
||||||
displayWatcher.setInitialised(true);
|
displayWatcher.setInitialised(true);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -394,30 +406,79 @@ var Display = {
|
|||||||
Reveal.slide(0);
|
Reveal.slide(0);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Set the transition type
|
* Enable/Disable item transitions
|
||||||
* @param {string} transitionType - Can be one of "none", "fade", "slide", "convex", "concave", "zoom"
|
|
||||||
* @param {string} transitionSpeed - Can be one of "default", "fast", "slow"
|
|
||||||
*/
|
*/
|
||||||
setTransition: function (transitionType, transitionSpeed) {
|
setItemTransition: function (enable) {
|
||||||
Reveal.configure({"transition": transitionType, "transitionSpeed": transitionSpeed});
|
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
|
* Clear the current list of slides
|
||||||
*/
|
*/
|
||||||
clearSlides: function () {
|
clearSlides: function () {
|
||||||
$(".slides")[0].innerHTML = "";
|
Display._slidesContainer.innerHTML = "";
|
||||||
|
Display._clearSlidesList();
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* Clear the current list of slides
|
||||||
|
*/
|
||||||
|
_clearSlidesList: function () {
|
||||||
|
Display._footerContainer.innerHTML = "";
|
||||||
Display._slides = {};
|
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
|
* Checks if the present slide content fits within the slide
|
||||||
*/
|
*/
|
||||||
doesContentFit: function () {
|
doesContentFit: function () {
|
||||||
var currSlide = $("section.text-slides");
|
var currSlide = $("section.text-slides");
|
||||||
if (currSlide.length === 0) {
|
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;
|
return currSlide.clientHeight >= currSlide.scrollHeight;
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -426,22 +487,17 @@ var Display = {
|
|||||||
* @param {string} image - Path to the splash image
|
* @param {string} image - Path to the splash image
|
||||||
*/
|
*/
|
||||||
setStartupSplashScreen: function(bg_color, image) {
|
setStartupSplashScreen: function(bg_color, image) {
|
||||||
Display.clearSlides();
|
Display._clearSlidesList();
|
||||||
var globalBackground = $("#global-background")[0];
|
|
||||||
globalBackground.style.cssText = "";
|
|
||||||
globalBackground.style.setProperty("background", bg_color);
|
|
||||||
var slidesDiv = $(".slides")[0];
|
|
||||||
var section = document.createElement("section");
|
var section = document.createElement("section");
|
||||||
section.setAttribute("id", 0);
|
section.setAttribute("id", 0);
|
||||||
section.setAttribute("data-background", bg_color);
|
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');
|
var img = document.createElement('img');
|
||||||
img.src = image;
|
img.src = image;
|
||||||
img.setAttribute("style", "position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-height: 100%; max-width: 100%");
|
img.setAttribute("style", "position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-height: 100%; max-width: 100%");
|
||||||
section.appendChild(img);
|
section.appendChild(img);
|
||||||
slidesDiv.appendChild(section);
|
|
||||||
Display._slides['0'] = 0;
|
Display._slides['0'] = 0;
|
||||||
Display.reinit();
|
Display.replaceSlides(section);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Set fullscreen image from path
|
* Set fullscreen image from path
|
||||||
@ -450,10 +506,6 @@ var Display = {
|
|||||||
*/
|
*/
|
||||||
setFullscreenImage: function(bg_color, image) {
|
setFullscreenImage: function(bg_color, image) {
|
||||||
Display.clearSlides();
|
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");
|
var section = document.createElement("section");
|
||||||
section.setAttribute("id", 0);
|
section.setAttribute("id", 0);
|
||||||
section.setAttribute("data-background", bg_color);
|
section.setAttribute("data-background", bg_color);
|
||||||
@ -462,9 +514,8 @@ var Display = {
|
|||||||
img.src = image;
|
img.src = image;
|
||||||
img.setAttribute("style", "height: 100%; width: 100%");
|
img.setAttribute("style", "height: 100%; width: 100%");
|
||||||
section.appendChild(img);
|
section.appendChild(img);
|
||||||
slidesDiv.appendChild(section);
|
|
||||||
Display._slides['0'] = 0;
|
Display._slides['0'] = 0;
|
||||||
Display.reinit();
|
Display.replaceSlides(parentSection);
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Set fullscreen image from base64 data
|
* Set fullscreen image from base64 data
|
||||||
@ -473,10 +524,6 @@ var Display = {
|
|||||||
*/
|
*/
|
||||||
setFullscreenImageFromData: function(bg_color, image_data) {
|
setFullscreenImageFromData: function(bg_color, image_data) {
|
||||||
Display.clearSlides();
|
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");
|
var section = document.createElement("section");
|
||||||
section.setAttribute("id", 0);
|
section.setAttribute("id", 0);
|
||||||
section.setAttribute("data-background", bg_color);
|
section.setAttribute("data-background", bg_color);
|
||||||
@ -485,7 +532,7 @@ var Display = {
|
|||||||
img.src = 'data:image/png;base64,' + image_data;
|
img.src = 'data:image/png;base64,' + image_data;
|
||||||
img.setAttribute("style", "height: 100%; width: 100%");
|
img.setAttribute("style", "height: 100%; width: 100%");
|
||||||
section.appendChild(img);
|
section.appendChild(img);
|
||||||
slidesDiv.appendChild(section);
|
Display._slidesContainer.appendChild(section);
|
||||||
Display._slides['0'] = 0;
|
Display._slides['0'] = 0;
|
||||||
Display.reinit();
|
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} verse - The verse number, e.g. "v1"
|
||||||
* @param {string} text - The HTML for the verse, e.g. "line1<br>line2"
|
* @param {string} text - The HTML for the verse, e.g. "line1<br>line2"
|
||||||
* @param {string} footer_text - The HTML for the footer
|
|
||||||
* @param {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 slide;
|
||||||
var html = _prepareText(text);
|
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 = document.createElement("section");
|
||||||
slide.setAttribute("id", verse);
|
slide.setAttribute("id", verse);
|
||||||
slide.innerHTML = html;
|
slide.innerHTML = html;
|
||||||
parent.appendChild(slide);
|
return slide;
|
||||||
Display._slides[verse] = parent.children.length - 1;
|
|
||||||
if (footerText) {
|
|
||||||
$(".footer")[0].innerHTML = footerText;
|
|
||||||
}
|
|
||||||
if (reinit) {
|
|
||||||
Display.reinit();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Set text slides.
|
* Set text slides.
|
||||||
* @param {Object[]} slides - A list of slides to add as JS objects: {"verse": "v1", "text": "line 1\nline2"}
|
* @param {Object[]} slides - A list of slides to add as JS objects: {"verse": "v1", "text": "line 1\nline2"}
|
||||||
*/
|
*/
|
||||||
setTextSlides: function (slides) {
|
setTextSlides: function (slides) {
|
||||||
Display.clearSlides();
|
Display._clearSlidesList();
|
||||||
|
var slide_html;
|
||||||
|
var parentSection = document.createElement("section");
|
||||||
|
parentSection.classList = "text-slides";
|
||||||
slides.forEach(function (slide) {
|
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 () {
|
setTextSlide: function (text) {
|
||||||
var slideContainer = document.createElement("section");
|
if (Display._slides.hasOwnProperty("test-slide") && Object.keys(Display._slides).length === 1) {
|
||||||
slideContainer.classList = "text-slides";
|
var slide = $("#" + "test-slide")[0];
|
||||||
var slidesDiv = $(".slides")[0];
|
var html = _prepareText(text);
|
||||||
slidesDiv.appendChild(slideContainer);
|
if (slide.innerHTML != html) {
|
||||||
// Apply the current theme to the new container
|
slide.innerHTML = html;
|
||||||
if (!!Display._theme) {
|
}
|
||||||
Display.setTheme(Display._theme);
|
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"}]
|
* @param {Object[]} slides - A list of images to add as JS objects [{"path": "url/to/file"}]
|
||||||
*/
|
*/
|
||||||
setImageSlides: function (slides) {
|
setImageSlides: function (slides) {
|
||||||
Display.clearSlides();
|
Display._clearSlidesList();
|
||||||
var slidesDiv = $(".slides")[0];
|
|
||||||
var parentSection = document.createElement("section");
|
var parentSection = document.createElement("section");
|
||||||
slides.forEach(function (slide, index) {
|
slides.forEach(function (slide, index) {
|
||||||
var section = document.createElement("section");
|
var section = document.createElement("section");
|
||||||
@ -710,15 +758,14 @@ var Display = {
|
|||||||
parentSection.appendChild(section);
|
parentSection.appendChild(section);
|
||||||
Display._slides[index.toString()] = index;
|
Display._slides[index.toString()] = index;
|
||||||
});
|
});
|
||||||
slidesDiv.appendChild(parentSection);
|
Display.replaceSlides(parentSection);
|
||||||
Display.reinit();
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Set a video
|
* Set a video
|
||||||
* @param {Object} video - The video to show as a JS object: {"path": "url/to/file"}
|
* @param {Object} video - The video to show as a JS object: {"path": "url/to/file"}
|
||||||
*/
|
*/
|
||||||
setVideo: function (video) {
|
setVideo: function (video) {
|
||||||
Display.clearSlides();
|
Display._clearSlidesList();
|
||||||
var section = document.createElement("section");
|
var section = document.createElement("section");
|
||||||
section.setAttribute("data-background", "#000");
|
section.setAttribute("data-background", "#000");
|
||||||
var videoElement = document.createElement("video");
|
var videoElement = document.createElement("video");
|
||||||
@ -747,8 +794,7 @@ var Display = {
|
|||||||
mediaWatcher.has_muted(event.target.muted);
|
mediaWatcher.has_muted(event.target.muted);
|
||||||
});
|
});
|
||||||
section.appendChild(videoElement);
|
section.appendChild(videoElement);
|
||||||
$(".slides")[0].appendChild(section);
|
Display.replaceSlides(section);
|
||||||
Display.reinit();
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Play a video
|
* Play a video
|
||||||
@ -856,32 +902,45 @@ var Display = {
|
|||||||
/**
|
/**
|
||||||
* Blank the screen
|
* Blank the screen
|
||||||
*/
|
*/
|
||||||
blankToBlack: function () {
|
toBlack: function () {
|
||||||
|
var documentBody = $("body")[0];
|
||||||
|
documentBody.style.opacity = 1;
|
||||||
if (!Reveal.isPaused()) {
|
if (!Reveal.isPaused()) {
|
||||||
Reveal.togglePause();
|
Reveal.togglePause();
|
||||||
}
|
}
|
||||||
// var slidesDiv = $(".slides")[0];
|
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Blank to theme
|
* Hide all but theme background
|
||||||
*/
|
*/
|
||||||
blankToTheme: function () {
|
toTheme: function () {
|
||||||
var slidesDiv = $(".slides")[0];
|
var documentBody = $("body")[0];
|
||||||
slidesDiv.style.visibility = "hidden";
|
documentBody.style.opacity = 1;
|
||||||
var footerDiv = $(".footer")[0];
|
Display._slidesContainer.style.opacity = 0;
|
||||||
footerDiv.style.visibility = "hidden";
|
Display._footerContainer.style.opacity = 0;
|
||||||
if (Reveal.isPaused()) {
|
if (Reveal.isPaused()) {
|
||||||
Reveal.togglePause();
|
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 the screen
|
||||||
*/
|
*/
|
||||||
show: function () {
|
show: function () {
|
||||||
var slidesDiv = $(".slides")[0];
|
var documentBody = $("body")[0];
|
||||||
slidesDiv.style.visibility = "visible";
|
documentBody.style.opacity = 1;
|
||||||
var footerDiv = $(".footer")[0];
|
Display._slidesContainer.style.opacity = 1;
|
||||||
footerDiv.style.visibility = "visible";
|
Display._footerContainer.style.opacity = 1;
|
||||||
if (Reveal.isPaused()) {
|
if (Reveal.isPaused()) {
|
||||||
Reveal.togglePause();
|
Reveal.togglePause();
|
||||||
}
|
}
|
||||||
@ -903,13 +962,31 @@ var Display = {
|
|||||||
var dh = parseFloat(_getStyle(d, "height"));
|
var dh = parseFloat(_getStyle(d, "height"));
|
||||||
return Math.floor(dh / lh);
|
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) {
|
setTheme: function (theme) {
|
||||||
|
if (Display._theme != theme) {
|
||||||
|
Display._themeApplied = false;
|
||||||
Display._theme = theme;
|
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
|
// Set slide transitions
|
||||||
var new_transition_type = "none",
|
var new_transition_type = "none",
|
||||||
new_transition_speed = "default";
|
new_transition_speed = "default";
|
||||||
if (!!theme.display_slide_transition && Display._doTransitions) {
|
if (!!Display._theme.display_slide_transition && Display._doTransitions) {
|
||||||
switch (theme.display_slide_transition_type) {
|
switch (Display._theme.display_slide_transition_type) {
|
||||||
case TransitionType.Fade:
|
case TransitionType.Fade:
|
||||||
new_transition_type = "fade";
|
new_transition_type = "fade";
|
||||||
break;
|
break;
|
||||||
@ -928,7 +1005,7 @@ var Display = {
|
|||||||
default:
|
default:
|
||||||
new_transition_type = "fade";
|
new_transition_type = "fade";
|
||||||
}
|
}
|
||||||
switch (theme.display_slide_transition_speed) {
|
switch (Display._theme.display_slide_transition_speed) {
|
||||||
case TransitionSpeed.Normal:
|
case TransitionSpeed.Normal:
|
||||||
new_transition_speed = "default";
|
new_transition_speed = "default";
|
||||||
break;
|
break;
|
||||||
@ -941,7 +1018,7 @@ var Display = {
|
|||||||
default:
|
default:
|
||||||
new_transition_speed = "default";
|
new_transition_speed = "default";
|
||||||
}
|
}
|
||||||
switch (theme.display_slide_transition_direction) {
|
switch (Display._theme.display_slide_transition_direction) {
|
||||||
case TransitionDirection.Vertical:
|
case TransitionDirection.Vertical:
|
||||||
new_transition_type += "-vertical";
|
new_transition_type += "-vertical";
|
||||||
break;
|
break;
|
||||||
@ -949,95 +1026,102 @@ var Display = {
|
|||||||
default:
|
default:
|
||||||
new_transition_type += "-horizontal";
|
new_transition_type += "-horizontal";
|
||||||
}
|
}
|
||||||
if (theme.display_slide_transition_reverse) {
|
if (Display._theme.display_slide_transition_reverse) {
|
||||||
new_transition_type += "-reverse";
|
new_transition_type += "-reverse";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
var slides = targetElement.children;
|
||||||
Display.setTransition(new_transition_type, new_transition_speed);
|
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
|
// Set the background
|
||||||
var globalBackground = $("#global-background")[0];
|
var backgroundContent = "";
|
||||||
var backgroundStyle = {};
|
|
||||||
var backgroundHtml = "";
|
var backgroundHtml = "";
|
||||||
switch (theme.background_type) {
|
var globalBackground = $("#global-background")[0];
|
||||||
|
globalBackground.style.setProperty("background", "black");
|
||||||
|
switch (Display._theme.background_type) {
|
||||||
case BackgroundType.Transparent:
|
case BackgroundType.Transparent:
|
||||||
backgroundStyle.background = "transparent";
|
backgroundContent = "transparent";
|
||||||
|
globalBackground.style.setProperty("background", "transparent");
|
||||||
break;
|
break;
|
||||||
case BackgroundType.Solid:
|
case BackgroundType.Solid:
|
||||||
backgroundStyle.background = theme.background_color;
|
backgroundContent = Display._theme.background_color;
|
||||||
break;
|
break;
|
||||||
case BackgroundType.Gradient:
|
case BackgroundType.Gradient:
|
||||||
switch (theme.background_direction) {
|
switch (Display._theme.background_direction) {
|
||||||
case GradientType.Horizontal:
|
case GradientType.Horizontal:
|
||||||
backgroundStyle.background = _buildLinearGradient("left top", "left bottom",
|
backgroundContent = _buildLinearGradient("left top", "left bottom",
|
||||||
theme.background_start_color,
|
Display._theme.background_start_color,
|
||||||
theme.background_end_color);
|
Display._theme.background_end_color);
|
||||||
break;
|
break;
|
||||||
case GradientType.Vertical:
|
case GradientType.Vertical:
|
||||||
backgroundStyle.background = _buildLinearGradient("left top", "right top",
|
backgroundContent = _buildLinearGradient("left top", "right top",
|
||||||
theme.background_start_color,
|
Display._theme.background_start_color,
|
||||||
theme.background_end_color);
|
Display._theme.background_end_color);
|
||||||
break;
|
break;
|
||||||
case GradientType.LeftTop:
|
case GradientType.LeftTop:
|
||||||
backgroundStyle.background = _buildLinearGradient("left top", "right bottom",
|
backgroundContent = _buildLinearGradient("left top", "right bottom",
|
||||||
theme.background_start_color,
|
Display._theme.background_start_color,
|
||||||
theme.background_end_color);
|
Display._theme.background_end_color);
|
||||||
break;
|
break;
|
||||||
case GradientType.LeftBottom:
|
case GradientType.LeftBottom:
|
||||||
backgroundStyle.background = _buildLinearGradient("left bottom", "right top",
|
backgroundContent = _buildLinearGradient("left bottom", "right top",
|
||||||
theme.background_start_color,
|
Display._theme.background_start_color,
|
||||||
theme.background_end_color);
|
Display._theme.background_end_color);
|
||||||
break;
|
break;
|
||||||
case GradientType.Circular:
|
case GradientType.Circular:
|
||||||
backgroundStyle.background = _buildRadialGradient(window.innerWidth / 2, theme.background_start_color,
|
backgroundContent = _buildRadialGradient(window.innerWidth / 2, Display._theme.background_start_color,
|
||||||
theme.background_end_color);
|
Display._theme.background_end_color);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
backgroundStyle.background = "#000";
|
backgroundContent = "#000";
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case BackgroundType.Image:
|
case BackgroundType.Image:
|
||||||
backgroundStyle["background-image"] = "url('" + theme.background_filename + "')";
|
backgroundContent = "url('" + Display._theme.background_filename + "')";
|
||||||
console.warn(backgroundStyle["background-image"]);
|
console.warn(backgroundContent);
|
||||||
break;
|
break;
|
||||||
case BackgroundType.Video:
|
case BackgroundType.Video:
|
||||||
// never actually used since background type is overridden from video to transparent in window.py
|
// never actually used since background type is overridden from video to transparent in window.py
|
||||||
backgroundStyle["background-color"] = theme.background_border_color;
|
backgroundContent = Display._theme.background_border_color;
|
||||||
backgroundHtml = "<video loop autoplay muted><source src='" + theme.background_filename + "'></video>";
|
backgroundHtml = "<video loop autoplay muted><source src='" + Display._theme.background_filename + "'></video>";
|
||||||
console.warn(backgroundHtml);
|
console.warn(backgroundHtml);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
backgroundStyle.background = "#000";
|
backgroundContent = "#000";
|
||||||
}
|
|
||||||
globalBackground.style.cssText = "";
|
|
||||||
for (var bgKey in backgroundStyle) {
|
|
||||||
if (backgroundStyle.hasOwnProperty(bgKey)) {
|
|
||||||
globalBackground.style.setProperty(bgKey, backgroundStyle[bgKey]);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
targetElement.style.cssText = "";
|
||||||
|
targetElement.setAttribute("data-background", backgroundContent);
|
||||||
|
targetElement.setAttribute("data-background-size", "cover");
|
||||||
if (!!backgroundHtml) {
|
if (!!backgroundHtml) {
|
||||||
globalBackground.innerHTML = backgroundHtml;
|
background.innerHTML = backgroundHtml;
|
||||||
}
|
}
|
||||||
|
|
||||||
// set up the main area
|
// set up the main area
|
||||||
|
if (!is_text) {
|
||||||
|
// only transition and background for non text slides
|
||||||
|
return;
|
||||||
|
}
|
||||||
mainStyle = {};
|
mainStyle = {};
|
||||||
if (!!theme.font_main_outline) {
|
if (!!Display._theme.font_main_outline) {
|
||||||
mainStyle["-webkit-text-stroke"] = "" + theme.font_main_outline_size + "pt " +
|
mainStyle["-webkit-text-stroke"] = "" + Display._theme.font_main_outline_size + "pt " +
|
||||||
theme.font_main_outline_color;
|
Display._theme.font_main_outline_color;
|
||||||
mainStyle["-webkit-text-fill-color"] = theme.font_main_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
|
// 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.width = Display._theme.font_main_width + "px";
|
||||||
mainStyle.height = theme.font_main_height + "px";
|
mainStyle.height = Display._theme.font_main_height + "px";
|
||||||
mainStyle["margin-top"] = "" + theme.font_main_y + "px";
|
mainStyle["margin-top"] = "" + Display._theme.font_main_y + "px";
|
||||||
mainStyle.left = "" + theme.font_main_x + "px";
|
mainStyle.left = "" + Display._theme.font_main_x + "px";
|
||||||
mainStyle.color = theme.font_main_color;
|
mainStyle.color = Display._theme.font_main_color;
|
||||||
mainStyle["font-family"] = theme.font_main_name;
|
mainStyle["font-family"] = Display._theme.font_main_name;
|
||||||
mainStyle["font-size"] = "" + theme.font_main_size + "pt";
|
mainStyle["font-size"] = "" + Display._theme.font_main_size + "pt";
|
||||||
mainStyle["font-style"] = !!theme.font_main_italics ? "italic" : "";
|
mainStyle["font-style"] = !!Display._theme.font_main_italics ? "italic" : "";
|
||||||
mainStyle["font-weight"] = !!theme.font_main_bold ? "bold" : "";
|
mainStyle["font-weight"] = !!Display._theme.font_main_bold ? "bold" : "";
|
||||||
mainStyle["line-height"] = "" + (100 + theme.font_main_line_adjustment) + "%";
|
mainStyle["line-height"] = "" + (100 + Display._theme.font_main_line_adjustment) + "%";
|
||||||
// Using text-align-last because there is a <br> seperating each line
|
// 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:
|
case HorizontalAlign.Justify:
|
||||||
mainStyle["text-align"] = "justify";
|
mainStyle["text-align"] = "justify";
|
||||||
mainStyle["text-align-last"] = "justify";
|
mainStyle["text-align-last"] = "justify";
|
||||||
@ -1058,7 +1142,7 @@ var Display = {
|
|||||||
mainStyle["text-align"] = "center";
|
mainStyle["text-align"] = "center";
|
||||||
mainStyle["text-align-last"] = "center";
|
mainStyle["text-align-last"] = "center";
|
||||||
}
|
}
|
||||||
switch (theme.display_vertical_align) {
|
switch (Display._theme.display_vertical_align) {
|
||||||
case VerticalAlign.Middle:
|
case VerticalAlign.Middle:
|
||||||
mainStyle["justify-content"] = "center";
|
mainStyle["justify-content"] = "center";
|
||||||
break;
|
break;
|
||||||
@ -1068,23 +1152,19 @@ var Display = {
|
|||||||
case VerticalAlign.Bottom:
|
case VerticalAlign.Bottom:
|
||||||
mainStyle["justify-content"] = "flex-end";
|
mainStyle["justify-content"] = "flex-end";
|
||||||
// This gets around the webkit scroll height bug
|
// 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;
|
break;
|
||||||
default:
|
default:
|
||||||
mainStyle["justify-content"] = "center";
|
mainStyle["justify-content"] = "center";
|
||||||
}
|
}
|
||||||
if (theme.hasOwnProperty('font_main_shadow_size') && !!theme.font_main_shadow) {
|
if (Display._theme.hasOwnProperty('font_main_shadow_size') && !!Display._theme.font_main_shadow) {
|
||||||
mainStyle["text-shadow"] = theme.font_main_shadow_color + " " + theme.font_main_shadow_size + "pt " +
|
mainStyle["text-shadow"] = Display._theme.font_main_shadow_color + " " + Display._theme.font_main_shadow_size + "pt " +
|
||||||
theme.font_main_shadow_size + "pt";
|
Display._theme.font_main_shadow_size + "pt";
|
||||||
}
|
}
|
||||||
var slidesDiv = $("section.text-slides");
|
targetElement.style.cssText = "";
|
||||||
if (slidesDiv.length > 0) {
|
|
||||||
slidesDiv = slidesDiv[0];
|
|
||||||
slidesDiv.style.cssText = "";
|
|
||||||
for (var mainKey in mainStyle) {
|
for (var mainKey in mainStyle) {
|
||||||
if (mainStyle.hasOwnProperty(mainKey)) {
|
if (mainStyle.hasOwnProperty(mainKey)) {
|
||||||
slidesDiv.style.setProperty(mainKey, mainStyle[mainKey]);
|
targetElement.style.setProperty(mainKey, mainStyle[mainKey]);
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Set up the footer
|
// Set up the footer
|
||||||
@ -1092,19 +1172,18 @@ var Display = {
|
|||||||
"text-align": "left"
|
"text-align": "left"
|
||||||
};
|
};
|
||||||
footerStyle.position = "absolute";
|
footerStyle.position = "absolute";
|
||||||
footerStyle.left = "" + theme.font_footer_x + "px";
|
footerStyle.left = "" + Display._theme.font_footer_x + "px";
|
||||||
footerStyle.top = "" + theme.font_footer_y + "px";
|
footerStyle.top = "" + Display._theme.font_footer_y + "px";
|
||||||
footerStyle.width = "" + theme.font_footer_width + "px";
|
footerStyle.width = "" + Display._theme.font_footer_width + "px";
|
||||||
footerStyle.height = "" + theme.font_footer_height + "px";
|
footerStyle.height = "" + Display._theme.font_footer_height + "px";
|
||||||
footerStyle.color = theme.font_footer_color;
|
footerStyle.color = Display._theme.font_footer_color;
|
||||||
footerStyle["font-family"] = theme.font_footer_name;
|
footerStyle["font-family"] = Display._theme.font_footer_name;
|
||||||
footerStyle["font-size"] = "" + theme.font_footer_size + "pt";
|
footerStyle["font-size"] = "" + Display._theme.font_footer_size + "pt";
|
||||||
footerStyle["white-space"] = theme.font_footer_wrap ? "normal" : "nowrap";
|
footerStyle["white-space"] = Display._theme.font_footer_wrap ? "normal" : "nowrap";
|
||||||
var footer = $(".footer")[0];
|
Display._footerContainer.style.cssText = "";
|
||||||
footer.style.cssText = "";
|
|
||||||
for (var footerKey in footerStyle) {
|
for (var footerKey in footerStyle) {
|
||||||
if (footerStyle.hasOwnProperty(footerKey)) {
|
if (footerStyle.hasOwnProperty(footerKey)) {
|
||||||
footer.style.setProperty(footerKey, footerStyle[footerKey]);
|
Display._footerContainer.style.setProperty(footerKey, footerStyle[footerKey]);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -812,7 +812,7 @@ class ThemePreviewRenderer(LogMixin, DisplayWindow):
|
|||||||
"""
|
"""
|
||||||
self.clear_slides()
|
self.clear_slides()
|
||||||
self.log_debug('_text_fits_on_slide: 1\n{text}'.format(text=text))
|
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)
|
.format(text=text.replace('"', '\\"')), is_sync=True)
|
||||||
self.log_debug('_text_fits_on_slide: 2')
|
self.log_debug('_text_fits_on_slide: 2')
|
||||||
does_text_fits = self.run_javascript('Display.doesContentFit();', is_sync=True)
|
does_text_fits = self.run_javascript('Display.doesContentFit();', is_sync=True)
|
||||||
|
@ -244,7 +244,9 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties):
|
|||||||
Add stuff after page initialisation
|
Add stuff after page initialisation
|
||||||
"""
|
"""
|
||||||
js_is_display = str(self.is_display).lower()
|
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)
|
wait_for(lambda: self._is_initialised)
|
||||||
if self.scale != 1:
|
if self.scale != 1:
|
||||||
self.set_scale(self.scale)
|
self.set_scale(self.scale)
|
||||||
@ -430,12 +432,6 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties):
|
|||||||
if self.is_display:
|
if self.is_display:
|
||||||
Registry().execute('live_display_active')
|
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):
|
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
|
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:
|
if mode == HideMode.Screen:
|
||||||
self.setVisible(False)
|
self.setVisible(False)
|
||||||
elif mode == HideMode.Blank:
|
elif mode == HideMode.Blank:
|
||||||
self.run_javascript('Display.blankToBlack();')
|
self.run_javascript('Display.toBlack();')
|
||||||
|
elif mode == HideMode.Theme:
|
||||||
|
self.run_javascript('Display.toTheme();')
|
||||||
else:
|
else:
|
||||||
self.run_javascript('Display.blankToTheme();')
|
self.run_javascript('Display.toTransparent();')
|
||||||
if mode != HideMode.Screen:
|
if mode != HideMode.Screen:
|
||||||
if self.isHidden():
|
if self.isHidden():
|
||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
|
@ -41,6 +41,7 @@ class HideMode(object):
|
|||||||
Blank = 1
|
Blank = 1
|
||||||
Theme = 2
|
Theme = 2
|
||||||
Screen = 3
|
Screen = 3
|
||||||
|
Transparent = 4
|
||||||
|
|
||||||
|
|
||||||
class DisplayControllerType(object):
|
class DisplayControllerType(object):
|
||||||
|
@ -40,7 +40,7 @@ from openlp.core.common.path import path_to_str
|
|||||||
from openlp.core.common.registry import Registry, RegistryBase
|
from openlp.core.common.registry import Registry, RegistryBase
|
||||||
from openlp.core.lib.serviceitem import ItemCapabilities
|
from openlp.core.lib.serviceitem import ItemCapabilities
|
||||||
from openlp.core.lib.ui import critical_error_message_box
|
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, \
|
from openlp.core.ui.media import MediaState, ItemMediaInfo, MediaType, parse_optical_path, parse_devicestream_path, \
|
||||||
VIDEO_EXT, AUDIO_EXT
|
VIDEO_EXT, AUDIO_EXT
|
||||||
from openlp.core.ui.media.remote import register_views
|
from openlp.core.ui.media.remote import register_views
|
||||||
@ -454,7 +454,9 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||||||
controller.media_info.is_playing = True
|
controller.media_info.is_playing = True
|
||||||
if not controller.media_info.is_background:
|
if not controller.media_info.is_background:
|
||||||
display = self._define_display(controller)
|
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
|
return True
|
||||||
|
|
||||||
def tick(self, controller):
|
def tick(self, controller):
|
||||||
@ -466,9 +468,14 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||||||
start_again = False
|
start_again = False
|
||||||
if controller.media_info.is_playing and controller.media_info.length > 0:
|
if controller.media_info.is_playing and controller.media_info.length > 0:
|
||||||
if controller.media_info.timer > controller.media_info.length:
|
if controller.media_info.timer > controller.media_info.length:
|
||||||
self.media_stop(controller)
|
|
||||||
if controller.media_info.is_looping_playback:
|
if controller.media_info.is_looping_playback:
|
||||||
start_again = True
|
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
|
controller.media_info.timer += TICK_TIME
|
||||||
seconds = controller.media_info.timer // 1000
|
seconds = controller.media_info.timer // 1000
|
||||||
minutes = seconds // 60
|
minutes = seconds // 60
|
||||||
@ -479,7 +486,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||||||
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
|
controller.position_label.setText(' %02d:%02d / %02d:%02d' %
|
||||||
(minutes, seconds, total_minutes, total_seconds))
|
(minutes, seconds, total_minutes, total_seconds))
|
||||||
if start_again:
|
if start_again:
|
||||||
self.media_play(controller, True)
|
controller.seek_slider.setSliderPosition(0)
|
||||||
|
self.media_play(controller, False)
|
||||||
|
|
||||||
def media_pause_msg(self, msg):
|
def media_pause_msg(self, msg):
|
||||||
"""
|
"""
|
||||||
@ -560,6 +568,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||||||
controller.media_info.is_playing = False
|
controller.media_info.is_playing = False
|
||||||
controller.media_info.timer = 1000
|
controller.media_info.timer = 1000
|
||||||
controller.media_timer = 0
|
controller.media_timer = 0
|
||||||
|
display = self._define_display(controller)
|
||||||
|
display.show_display()
|
||||||
|
|
||||||
def media_volume_msg(self, msg):
|
def media_volume_msg(self, msg):
|
||||||
"""
|
"""
|
||||||
@ -615,6 +625,8 @@ class MediaController(RegistryBase, LogMixin, RegistryProperties):
|
|||||||
"""
|
"""
|
||||||
self.set_controls_visible(controller, False)
|
self.set_controls_visible(controller, False)
|
||||||
if controller.controller_type in self.current_media_players:
|
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].reset(controller)
|
||||||
self.current_media_players[controller.controller_type].set_visible(controller, False)
|
self.current_media_players[controller.controller_type].set_visible(controller, False)
|
||||||
del self.current_media_players[controller.controller_type]
|
del self.current_media_players[controller.controller_type]
|
||||||
|
@ -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)
|
self.preview_display.set_theme(theme_data, service_item_type=service_item.service_item_type)
|
||||||
# Set theme for displays
|
# Set theme for displays
|
||||||
for display in self.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):
|
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()),
|
Registry().execute('{text}_slide'.format(text=self.service_item.name.lower()),
|
||||||
[self.service_item, self.is_live, row])
|
[self.service_item, self.is_live, row])
|
||||||
else:
|
else:
|
||||||
# to_display = self.service_item.get_rendered_frame(row)
|
|
||||||
if self.service_item.is_text():
|
|
||||||
for display in self.displays:
|
for display in self.displays:
|
||||||
display.go_to_slide(row)
|
display.go_to_slide(row)
|
||||||
# self.display.text(to_display, row != old_selected_row)
|
if not self.service_item.is_text():
|
||||||
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)
|
|
||||||
# reset the store used to display first image
|
# reset the store used to display first image
|
||||||
self.service_item.bg_image_bytes = None
|
self.service_item.bg_image_bytes = None
|
||||||
self.selected_row = row
|
self.selected_row = row
|
||||||
|
@ -69,6 +69,9 @@ class ThemesTab(SettingsTab):
|
|||||||
self.wrap_footer_check_box = QtWidgets.QCheckBox(self.universal_group_box)
|
self.wrap_footer_check_box = QtWidgets.QCheckBox(self.universal_group_box)
|
||||||
self.wrap_footer_check_box.setObjectName('wrap_footer_check_box')
|
self.wrap_footer_check_box.setObjectName('wrap_footer_check_box')
|
||||||
self.universal_group_box_layout.addWidget(self.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.addWidget(self.universal_group_box)
|
||||||
self.left_layout.addStretch()
|
self.left_layout.addStretch()
|
||||||
self.level_group_box = QtWidgets.QGroupBox(self.right_column)
|
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.global_group_box.setTitle(translate('OpenLP.ThemesTab', 'Global Theme'))
|
||||||
self.universal_group_box.setTitle(translate('OpenLP.ThemesTab', 'Universal Settings'))
|
self.universal_group_box.setTitle(translate('OpenLP.ThemesTab', 'Universal Settings'))
|
||||||
self.wrap_footer_check_box.setText(translate('OpenLP.ThemesTab', '&Wrap footer text'))
|
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.level_group_box.setTitle(translate('OpenLP.ThemesTab', 'Theme Level'))
|
||||||
self.song_level_radio_button.setText(translate('OpenLP.ThemesTab', 'S&ong Level'))
|
self.song_level_radio_button.setText(translate('OpenLP.ThemesTab', 'S&ong Level'))
|
||||||
self.song_level_label.setText(
|
self.song_level_label.setText(
|
||||||
@ -139,6 +143,7 @@ class ThemesTab(SettingsTab):
|
|||||||
self.theme_level = self.settings.value('theme level')
|
self.theme_level = self.settings.value('theme level')
|
||||||
self.global_theme = self.settings.value('global theme')
|
self.global_theme = self.settings.value('global theme')
|
||||||
self.wrap_footer_check_box.setChecked(self.settings.value('wrap footer'))
|
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()
|
self.settings.endGroup()
|
||||||
if self.theme_level == ThemeLevel.Global:
|
if self.theme_level == ThemeLevel.Global:
|
||||||
self.global_level_radio_button.setChecked(True)
|
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('theme level', self.theme_level)
|
||||||
self.settings.setValue('global theme', self.global_theme)
|
self.settings.setValue('global theme', self.global_theme)
|
||||||
self.settings.setValue('wrap footer', self.wrap_footer_check_box.isChecked())
|
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.settings.endGroup()
|
||||||
self.renderer.set_theme_level(self.theme_level)
|
self.renderer.set_theme_level(self.theme_level)
|
||||||
if self.tab_visited:
|
if self.tab_visited:
|
||||||
|
@ -32,6 +32,7 @@ from PyQt5 import QtCore
|
|||||||
sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
||||||
|
|
||||||
from openlp.core.display.window import DisplayWindow
|
from openlp.core.display.window import DisplayWindow
|
||||||
|
from openlp.core.ui import HideMode
|
||||||
|
|
||||||
|
|
||||||
@patch('PyQt5.QtWidgets.QVBoxLayout')
|
@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);')
|
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('PyQt5.QtWidgets.QVBoxLayout')
|
||||||
@patch('openlp.core.display.webengine.WebEngineView')
|
@patch('openlp.core.display.webengine.WebEngineView')
|
||||||
@patch.object(time, 'time')
|
@patch.object(time, 'time')
|
||||||
@ -146,3 +172,75 @@ def test_run_javascript_sync_no_wait(mock_time, mocked_webengine, mocked_addWidg
|
|||||||
assert result == 1234
|
assert result == 1234
|
||||||
webengine_page.runJavaScript.assert_called_once()
|
webengine_page.runJavaScript.assert_called_once()
|
||||||
mock_time.sleep.assert_not_called()
|
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
|
||||||
|
@ -324,3 +324,23 @@ def test_setup_display(MockItemMediaInfo, media_env):
|
|||||||
assert controller.has_audio is False
|
assert controller.has_audio is False
|
||||||
media_env.media_controller._define_display.assert_called_once_with(controller)
|
media_env.media_controller._define_display.assert_called_once_with(controller)
|
||||||
media_env.media_controller.vlc_player.setup(controller, mocked_display, False)
|
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()
|
||||||
|
@ -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'
|
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):
|
def test_text_service_item_blank(settings):
|
||||||
"""
|
"""
|
||||||
Test that loading a text-based service item into the slide controller sets the correct blank menu
|
Test that loading a text-based service item into the slide controller sets the correct blank menu
|
||||||
|
@ -76,6 +76,7 @@ describe("The function", function () {
|
|||||||
});
|
});
|
||||||
|
|
||||||
describe("The Display object", function () {
|
describe("The Display object", function () {
|
||||||
|
|
||||||
it("should start with a blank _slides object", function () {
|
it("should start with a blank _slides object", function () {
|
||||||
expect(Display._slides).toEqual({});
|
expect(Display._slides).toEqual({});
|
||||||
});
|
});
|
||||||
@ -107,6 +108,8 @@ describe("The Display object", function () {
|
|||||||
|
|
||||||
it("should initialise Reveal when init is called", function () {
|
it("should initialise Reveal when init is called", function () {
|
||||||
spyOn(Reveal, "initialize");
|
spyOn(Reveal, "initialize");
|
||||||
|
document.body.innerHTML = "";
|
||||||
|
_createDiv({"id": "global-background"});
|
||||||
Display.init();
|
Display.init();
|
||||||
expect(Reveal.initialize).toHaveBeenCalled();
|
expect(Reveal.initialize).toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
@ -123,16 +126,18 @@ describe("The Display object", function () {
|
|||||||
expect(Reveal.slide).toHaveBeenCalledWith(0);
|
expect(Reveal.slide).toHaveBeenCalledWith(0);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should have a setTransition() method", function () {
|
it("should have a setItemTransition() method", function () {
|
||||||
expect(Display.setTransition).toBeDefined();
|
expect(Display.setItemTransition).toBeDefined();
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should have a correctly functioning clearSlides() method", function () {
|
it("should have a correctly functioning clearSlides() method", function () {
|
||||||
expect(Display.clearSlides).toBeDefined();
|
expect(Display.clearSlides).toBeDefined();
|
||||||
|
|
||||||
document.body.innerHTML = "";
|
|
||||||
var slidesDiv = _createDiv({"class": "slides"});
|
var slidesDiv = _createDiv({"class": "slides"});
|
||||||
slidesDiv.innerHTML = "<section><p></p></section>";
|
slidesDiv.innerHTML = "<section><p></p></section>";
|
||||||
|
Display._slidesContainer = slidesDiv;
|
||||||
|
var footerDiv = _createDiv({"class": "footer"});
|
||||||
|
Display._footerContainer = footerDiv;
|
||||||
|
|
||||||
Display.clearSlides();
|
Display.clearSlides();
|
||||||
expect($(".slides")[0].innerHTML).toEqual("");
|
expect($(".slides")[0].innerHTML).toEqual("");
|
||||||
@ -167,42 +172,48 @@ describe("Transitions", function () {
|
|||||||
Display._theme = null;
|
Display._theme = null;
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should have a correctly functioning setTransition() method", function () {
|
it("should have a correctly functioning setItemTransition() method", function () {
|
||||||
spyOn(Reveal, "configure");
|
spyOn(Reveal, "configure");
|
||||||
Display.setTransition("fade", "slow");
|
Display.setItemTransition(true);
|
||||||
expect(Reveal.configure).toHaveBeenCalledWith({"transition": "fade", "transitionSpeed": "slow"});
|
expect(Reveal.configure).toHaveBeenCalledWith({"backgroundTransition": "fade", "transitionSpeed": "default"});
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should have enabled transitions when _doTransitions is true and setTheme is run", function () {
|
it("should have enabled transitions when _doTransitions is true and applyTheme is run", function () {
|
||||||
spyOn(Display, "setTransition");
|
|
||||||
Display._doTransitions = true;
|
Display._doTransitions = true;
|
||||||
var theme = {
|
var theme = {
|
||||||
"display_slide_transition": true,
|
"display_slide_transition": true,
|
||||||
"display_slide_transition_type": TransitionType.Slide,
|
"display_slide_transition_type": TransitionType.Slide,
|
||||||
"display_slide_transition_speed": TransitionSpeed.Fast
|
"display_slide_transition_speed": TransitionSpeed.Fast
|
||||||
}
|
}
|
||||||
|
|
||||||
Display.setTheme(theme);
|
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 () {
|
it("should have not enabled transitions when init() with no transitions and setTheme is run", function () {
|
||||||
spyOn(Display, "setTransition");
|
|
||||||
Display._doTransitions = false;
|
Display._doTransitions = false;
|
||||||
var theme = {
|
var theme = {
|
||||||
"display_slide_transition": true,
|
"display_slide_transition": true,
|
||||||
"display_slide_transition_type": TransitionType.Slide,
|
"display_slide_transition_type": TransitionType.Slide,
|
||||||
"display_slide_transition_speed": TransitionSpeed.Fast,
|
"display_slide_transition_speed": TransitionSpeed.Fast,
|
||||||
}
|
}
|
||||||
|
|
||||||
Display.setTheme(theme);
|
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 () {
|
it("should have enabled transitions in the correct direction", function () {
|
||||||
spyOn(Display, "setTransition");
|
|
||||||
Display._doTransitions = true;
|
Display._doTransitions = true;
|
||||||
var theme = {
|
var theme = {
|
||||||
"display_slide_transition": true,
|
"display_slide_transition": true,
|
||||||
@ -211,10 +222,15 @@ describe("Transitions", function () {
|
|||||||
"display_slide_transition_direction": TransitionDirection.Vertical,
|
"display_slide_transition_direction": TransitionDirection.Vertical,
|
||||||
"display_slide_transition_reverse": true,
|
"display_slide_transition_reverse": true,
|
||||||
}
|
}
|
||||||
|
|
||||||
Display.setTheme(theme);
|
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() {
|
beforeEach(function() {
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = "";
|
||||||
_createDiv({"class": "slides"});
|
var slides_container = _createDiv({"class": "slides"});
|
||||||
_createDiv({"class": "footer"});
|
var footer_container = _createDiv({"class": "footer"});
|
||||||
|
Display._slidesContainer = slides_container;
|
||||||
|
Display._footerContainer = footer_container;
|
||||||
Display._slides = {};
|
Display._slides = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add a new slide", function () {
|
it("should add a new slide", function () {
|
||||||
var verse = "v1",
|
var text = "Amazing grace,\nhow sweet the sound";
|
||||||
text = "Amazing grace,\nhow sweet the sound",
|
|
||||||
footer = "Public Domain";
|
|
||||||
spyOn(Display, "reinit");
|
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").length).toEqual(1);
|
||||||
expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
|
expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
|
||||||
expect(Display.reinit).toHaveBeenCalled();
|
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 () {
|
it("should update an existing slide", function () {
|
||||||
var verse = "v1",
|
var text = "That saved a wretch\nlike me";
|
||||||
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);
|
|
||||||
spyOn(Display, "reinit");
|
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").length).toEqual(1);
|
||||||
expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
|
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 () {
|
describe("Display.setTextSlides", function () {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = "";
|
||||||
_createDiv({"class": "slides"});
|
var slides_container = _createDiv({"class": "slides"});
|
||||||
_createDiv({"class": "footer"});
|
var footer_container = _createDiv({"class": "footer"});
|
||||||
|
Display._slidesContainer = slides_container;
|
||||||
|
Display._footerContainer = footer_container;
|
||||||
_createDiv({"id": "global-background"});
|
_createDiv({"id": "global-background"});
|
||||||
Display._slides = {};
|
Display._slides = {};
|
||||||
});
|
});
|
||||||
@ -581,15 +583,16 @@ describe("Display.setTextSlides", function () {
|
|||||||
}
|
}
|
||||||
];
|
];
|
||||||
spyOn(Display, "clearSlides");
|
spyOn(Display, "clearSlides");
|
||||||
spyOn(Display, "reinit");
|
spyOn(Reveal, "sync");
|
||||||
|
spyOn(Reveal, "slide");
|
||||||
|
|
||||||
Display.setTextSlides(slides);
|
Display.setTextSlides(slides);
|
||||||
|
|
||||||
expect(Display.clearSlides).toHaveBeenCalledTimes(1);
|
expect(Display.clearSlides).toHaveBeenCalledTimes(0);
|
||||||
expect(Display._slides["v1"]).toEqual(0);
|
expect(Display._slides["v1"]).toEqual(0);
|
||||||
expect(Display._slides["v2"]).toEqual(1);
|
expect(Display._slides["v2"]).toEqual(1);
|
||||||
expect($(".slides > section > section").length).toEqual(2);
|
expect($(".slides > section > section").length).toEqual(2);
|
||||||
expect(Display.reinit).toHaveBeenCalledTimes(1);
|
expect(Reveal.sync).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should correctly set outline width", function () {
|
it("should correctly set outline width", function () {
|
||||||
@ -607,7 +610,7 @@ describe("Display.setTextSlides", function () {
|
|||||||
'font_main_outline_size': 42,
|
'font_main_outline_size': 42,
|
||||||
'font_main_outline_color': 'red'
|
'font_main_outline_color': 'red'
|
||||||
};
|
};
|
||||||
spyOn(Display, "reinit");
|
spyOn(Reveal, "sync");
|
||||||
spyOn(Reveal, "slide");
|
spyOn(Reveal, "slide");
|
||||||
|
|
||||||
Display.setTheme(theme);
|
Display.setTheme(theme);
|
||||||
@ -633,7 +636,7 @@ describe("Display.setTextSlides", function () {
|
|||||||
'display_horizontal_align': 3,
|
'display_horizontal_align': 3,
|
||||||
'display_vertical_align': 1
|
'display_vertical_align': 1
|
||||||
};
|
};
|
||||||
spyOn(Display, "reinit");
|
spyOn(Reveal, "sync");
|
||||||
spyOn(Reveal, "slide");
|
spyOn(Reveal, "slide");
|
||||||
|
|
||||||
Display.setTheme(theme);
|
Display.setTheme(theme);
|
||||||
@ -659,7 +662,7 @@ describe("Display.setTextSlides", function () {
|
|||||||
'font_main_shadow_color': "#000",
|
'font_main_shadow_color': "#000",
|
||||||
'font_main_shadow_size': 5
|
'font_main_shadow_size': 5
|
||||||
};
|
};
|
||||||
spyOn(Display, "reinit");
|
spyOn(Reveal, "sync");
|
||||||
spyOn(Reveal, "slide");
|
spyOn(Reveal, "slide");
|
||||||
|
|
||||||
Display.setTheme(theme);
|
Display.setTheme(theme);
|
||||||
@ -684,7 +687,7 @@ describe("Display.setTextSlides", function () {
|
|||||||
'font_main_shadow_color': "#000",
|
'font_main_shadow_color': "#000",
|
||||||
'font_main_shadow_size': 5
|
'font_main_shadow_size': 5
|
||||||
};
|
};
|
||||||
spyOn(Display, "reinit");
|
spyOn(Reveal, "sync");
|
||||||
spyOn(Reveal, "slide");
|
spyOn(Reveal, "slide");
|
||||||
|
|
||||||
Display.setTheme(theme);
|
Display.setTheme(theme);
|
||||||
@ -710,7 +713,7 @@ describe("Display.setTextSlides", function () {
|
|||||||
'font_main_width': 1230,
|
'font_main_width': 1230,
|
||||||
'font_main_height': 4560
|
'font_main_height': 4560
|
||||||
};
|
};
|
||||||
spyOn(Display, "reinit");
|
spyOn(Reveal, "sync");
|
||||||
spyOn(Reveal, "slide");
|
spyOn(Reveal, "slide");
|
||||||
|
|
||||||
Display.setTheme(theme);
|
Display.setTheme(theme);
|
||||||
@ -727,20 +730,21 @@ describe("Display.setTextSlides", function () {
|
|||||||
describe("Display.setImageSlides", function () {
|
describe("Display.setImageSlides", function () {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = "";
|
||||||
_createDiv({"class": "slides"});
|
var slides_container = _createDiv({"class": "slides"});
|
||||||
_createDiv({"class": "footer"});
|
var footer_container = _createDiv({"class": "footer"});
|
||||||
|
Display._slidesContainer = slides_container;
|
||||||
|
Display._footerContainer = footer_container;
|
||||||
_createDiv({"id": "global-background"});
|
_createDiv({"id": "global-background"});
|
||||||
Display._slides = {};
|
Display._slides = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add a list of images", function () {
|
it("should add a list of images", function () {
|
||||||
var slides = [{"path": "file:///openlp1.jpg"}, {"path": "file:///openlp2.jpg"}];
|
var slides = [{"path": "file:///openlp1.jpg"}, {"path": "file:///openlp2.jpg"}];
|
||||||
spyOn(Display, "clearSlides");
|
spyOn(Reveal, "sync");
|
||||||
spyOn(Display, "reinit");
|
spyOn(Reveal, "slide");
|
||||||
|
|
||||||
Display.setImageSlides(slides);
|
Display.setImageSlides(slides);
|
||||||
|
|
||||||
expect(Display.clearSlides).toHaveBeenCalledTimes(1);
|
|
||||||
expect(Display._slides["0"]).toEqual(0);
|
expect(Display._slides["0"]).toEqual(0);
|
||||||
expect(Display._slides["1"]).toEqual(1);
|
expect(Display._slides["1"]).toEqual(1);
|
||||||
expect($(".slides > section > section").length).toEqual(2);
|
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")[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("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($(".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 () {
|
describe("Display.setVideo", function () {
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = "";
|
||||||
_createDiv({"class": "slides"});
|
var slides_container = _createDiv({"class": "slides"});
|
||||||
|
Display._slidesContainer = slides_container;
|
||||||
_createDiv({"id": "global-background"});
|
_createDiv({"id": "global-background"});
|
||||||
Display._slides = {};
|
Display._slides = {};
|
||||||
});
|
});
|
||||||
|
|
||||||
it("should add a video to the page", function () {
|
it("should add a video to the page", function () {
|
||||||
var video = {"path": "file:///video.mp4"};
|
var video = {"path": "file:///video.mp4"};
|
||||||
spyOn(Display, "clearSlides");
|
spyOn(Reveal, "sync");
|
||||||
spyOn(Display, "reinit");
|
spyOn(Reveal, "slide");
|
||||||
|
|
||||||
Display.setVideo(video);
|
Display.setVideo(video);
|
||||||
|
|
||||||
expect(Display.clearSlides).toHaveBeenCalledTimes(1);
|
|
||||||
expect($(".slides > section").length).toEqual(1);
|
expect($(".slides > section").length).toEqual(1);
|
||||||
expect($(".slides > section > video").length).toEqual(1);
|
expect($(".slides > section > video").length).toEqual(1);
|
||||||
expect($(".slides > section > video")[0].src).toEqual("file:///video.mp4")
|
expect($(".slides > section > video")[0].src).toEqual("file:///video.mp4")
|
||||||
expect(Display.reinit).toHaveBeenCalledTimes(1);
|
expect(Reveal.sync).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user