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/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,
|
||||
|
@ -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 {
|
||||
|
@ -352,6 +352,9 @@ AudioPlayer.prototype.stop = function () {
|
||||
* The Display object is what we use from OpenLP
|
||||
*/
|
||||
var Display = {
|
||||
_slidesContainer: null,
|
||||
_footerContainer: null,
|
||||
_backgroundsContainer: null,
|
||||
_alerts: [],
|
||||
_slides: {},
|
||||
_alertSettings: {},
|
||||
@ -359,6 +362,8 @@ var Display = {
|
||||
_transitionState: TransitionState.NoTransition,
|
||||
_animationState: AnimationState.NoAnimation,
|
||||
_doTransitions: false,
|
||||
_doItemTransitions: false,
|
||||
_themeApplied: true,
|
||||
_revealConfig: {
|
||||
margin: 0.0,
|
||||
minScale: 1.0,
|
||||
@ -380,9 +385,16 @@ var Display = {
|
||||
/**
|
||||
* Start up reveal and do any other initialisation
|
||||
*/
|
||||
init: function (doTransitions=false) {
|
||||
init: function (doTransitions=false, doItemtransitions=false) {
|
||||
var globalBackground = $("#global-background")[0];
|
||||
globalBackground.style.cssText = "";
|
||||
globalBackground.style.setProperty("background", "black");
|
||||
Display._slidesContainer = $(".slides")[0];
|
||||
Display._footerContainer = $(".footer")[0];
|
||||
Display._backgroundsContainer = $(".backgrounds")[0];
|
||||
Display._doTransitions = doTransitions;
|
||||
Reveal.initialize(Display._revealConfig);
|
||||
Display.setItemTransition(doItemtransitions && doTransitions);
|
||||
displayWatcher.setInitialised(true);
|
||||
},
|
||||
/**
|
||||
@ -394,30 +406,79 @@ var Display = {
|
||||
Reveal.slide(0);
|
||||
},
|
||||
/**
|
||||
* Set the transition type
|
||||
* @param {string} transitionType - Can be one of "none", "fade", "slide", "convex", "concave", "zoom"
|
||||
* @param {string} transitionSpeed - Can be one of "default", "fast", "slow"
|
||||
* Enable/Disable item transitions
|
||||
*/
|
||||
setTransition: function (transitionType, transitionSpeed) {
|
||||
Reveal.configure({"transition": transitionType, "transitionSpeed": transitionSpeed});
|
||||
setItemTransition: function (enable) {
|
||||
Display._doItemTransitions = enable;
|
||||
var body = $("body")[0];
|
||||
if (enable) {
|
||||
body.classList.add("transition");
|
||||
Reveal.configure({"backgroundTransition": "fade", "transitionSpeed": "default"});
|
||||
} else {
|
||||
body.classList.remove("transition");
|
||||
Reveal.configure({"backgroundTransition": "none"});
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Clear the current list of slides
|
||||
*/
|
||||
clearSlides: function () {
|
||||
$(".slides")[0].innerHTML = "";
|
||||
Display._slidesContainer.innerHTML = "";
|
||||
Display._clearSlidesList();
|
||||
},
|
||||
/**
|
||||
* Clear the current list of slides
|
||||
*/
|
||||
_clearSlidesList: function () {
|
||||
Display._footerContainer.innerHTML = "";
|
||||
Display._slides = {};
|
||||
},
|
||||
/**
|
||||
* Add new item/slides, replacing the old one
|
||||
* Clears current list of slides but allows time for a transition animation
|
||||
* Items are ordered newest to oldest in the slides container
|
||||
* @param {element} new_slides - New slides to display
|
||||
* @param {element} is_text - Used to decide if the theme main area constraints should apply
|
||||
*/
|
||||
replaceSlides: function (new_slides, is_text=false) {
|
||||
if (Display._doItemTransitions) {
|
||||
new_slides.setAttribute('data-transition', "fade");
|
||||
new_slides.setAttribute('data-transition-speed', "default");
|
||||
}
|
||||
new_slides.classList.add("future");
|
||||
Display.applyTheme(new_slides, is_text);
|
||||
Display._slidesContainer.prepend(new_slides);
|
||||
var currentSlide = Reveal.getIndices();
|
||||
if (Display._doItemTransitions && Display._slidesContainer.children.length >= 2) {
|
||||
// Set the slide one section ahead so we'll stay on the old slide after reinit
|
||||
Reveal.slide(1, currentSlide.v);
|
||||
Display.reinit();
|
||||
// Timeout to allow time to transition before deleting the old slides
|
||||
setTimeout (Display._removeLastSection, 5000);
|
||||
} else {
|
||||
Reveal.slide(0, currentSlide.v);
|
||||
Reveal.sync();
|
||||
Display._removeLastSection();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Removes the last slides item if there are more than one
|
||||
*/
|
||||
_removeLastSection: function () {
|
||||
if (Display._slidesContainer.children.length > 1) {
|
||||
Display._slidesContainer.lastChild.remove();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Checks if the present slide content fits within the slide
|
||||
*/
|
||||
doesContentFit: function () {
|
||||
var currSlide = $("section.text-slides");
|
||||
if (currSlide.length === 0) {
|
||||
currSlide = $(".slides");
|
||||
}
|
||||
currSlide = Display._footerContainer;
|
||||
} else {
|
||||
currSlide = currSlide[0];
|
||||
console.debug("scrollHeight: " + currSlide.scrollHeight + ", clientHeight: " + currSlide.clientHeight);
|
||||
}
|
||||
return currSlide.clientHeight >= currSlide.scrollHeight;
|
||||
},
|
||||
/**
|
||||
@ -426,22 +487,17 @@ var Display = {
|
||||
* @param {string} image - Path to the splash image
|
||||
*/
|
||||
setStartupSplashScreen: function(bg_color, image) {
|
||||
Display.clearSlides();
|
||||
var globalBackground = $("#global-background")[0];
|
||||
globalBackground.style.cssText = "";
|
||||
globalBackground.style.setProperty("background", bg_color);
|
||||
var slidesDiv = $(".slides")[0];
|
||||
Display._clearSlidesList();
|
||||
var section = document.createElement("section");
|
||||
section.setAttribute("id", 0);
|
||||
section.setAttribute("data-background", bg_color);
|
||||
section.setAttribute("style", "height: 100%; width: 100%; position: relative;");
|
||||
section.setAttribute("style", "height: 100%; width: 100%;");
|
||||
var img = document.createElement('img');
|
||||
img.src = image;
|
||||
img.setAttribute("style", "position: absolute; top: 0; bottom: 0; left: 0; right: 0; margin: auto; max-height: 100%; max-width: 100%");
|
||||
section.appendChild(img);
|
||||
slidesDiv.appendChild(section);
|
||||
Display._slides['0'] = 0;
|
||||
Display.reinit();
|
||||
Display.replaceSlides(section);
|
||||
},
|
||||
/**
|
||||
* Set fullscreen image from path
|
||||
@ -450,10 +506,6 @@ var Display = {
|
||||
*/
|
||||
setFullscreenImage: function(bg_color, image) {
|
||||
Display.clearSlides();
|
||||
var globalBackground = $("#global-background")[0];
|
||||
globalBackground.style.cssText = "";
|
||||
globalBackground.style.setProperty("background", bg_color);
|
||||
var slidesDiv = $(".slides")[0];
|
||||
var section = document.createElement("section");
|
||||
section.setAttribute("id", 0);
|
||||
section.setAttribute("data-background", bg_color);
|
||||
@ -462,9 +514,8 @@ var Display = {
|
||||
img.src = image;
|
||||
img.setAttribute("style", "height: 100%; width: 100%");
|
||||
section.appendChild(img);
|
||||
slidesDiv.appendChild(section);
|
||||
Display._slides['0'] = 0;
|
||||
Display.reinit();
|
||||
Display.replaceSlides(parentSection);
|
||||
},
|
||||
/**
|
||||
* Set fullscreen image from base64 data
|
||||
@ -473,10 +524,6 @@ var Display = {
|
||||
*/
|
||||
setFullscreenImageFromData: function(bg_color, image_data) {
|
||||
Display.clearSlides();
|
||||
var globalBackground = $("#global-background")[0];
|
||||
globalBackground.style.cssText = "";
|
||||
globalBackground.style.setProperty("background", bg_color);
|
||||
var slidesDiv = $(".slides")[0];
|
||||
var section = document.createElement("section");
|
||||
section.setAttribute("id", 0);
|
||||
section.setAttribute("data-background", bg_color);
|
||||
@ -485,7 +532,7 @@ var Display = {
|
||||
img.src = 'data:image/png;base64,' + image_data;
|
||||
img.setAttribute("style", "height: 100%; width: 100%");
|
||||
section.appendChild(img);
|
||||
slidesDiv.appendChild(section);
|
||||
Display._slidesContainer.appendChild(section);
|
||||
Display._slides['0'] = 0;
|
||||
Display.reinit();
|
||||
},
|
||||
@ -632,62 +679,64 @@ var Display = {
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Add a slide. If the slide exists but the HTML is different, update the slide.
|
||||
* Create a text slide.
|
||||
* @param {string} verse - The verse number, e.g. "v1"
|
||||
* @param {string} text - The HTML for the verse, e.g. "line1<br>line2"
|
||||
* @param {string} footer_text - The HTML for the footer
|
||||
* @param {bool} reinit - True if React should reinit when creating a new slide
|
||||
*/
|
||||
addTextSlide: function (verse, text, footerText, reinit=true) {
|
||||
_createTextSlide: function (verse, text) {
|
||||
var slide;
|
||||
var html = _prepareText(text);
|
||||
if (Display._slides.hasOwnProperty(verse)) {
|
||||
slide = $("#" + verse)[0];
|
||||
if (slide.innerHTML != html) {
|
||||
slide.innerHTML = html;
|
||||
}
|
||||
} else {
|
||||
var parent = $("section.text-slides");
|
||||
if (parent.length === 0) {
|
||||
Display._createTextContainer();
|
||||
parent = $("section.text-slides");
|
||||
}
|
||||
parent = parent[0];
|
||||
slide = document.createElement("section");
|
||||
slide.setAttribute("id", verse);
|
||||
slide.innerHTML = html;
|
||||
parent.appendChild(slide);
|
||||
Display._slides[verse] = parent.children.length - 1;
|
||||
if (footerText) {
|
||||
$(".footer")[0].innerHTML = footerText;
|
||||
}
|
||||
if (reinit) {
|
||||
Display.reinit();
|
||||
}
|
||||
}
|
||||
return slide;
|
||||
},
|
||||
/**
|
||||
* Set text slides.
|
||||
* @param {Object[]} slides - A list of slides to add as JS objects: {"verse": "v1", "text": "line 1\nline2"}
|
||||
*/
|
||||
setTextSlides: function (slides) {
|
||||
Display.clearSlides();
|
||||
Display._clearSlidesList();
|
||||
var slide_html;
|
||||
var parentSection = document.createElement("section");
|
||||
parentSection.classList = "text-slides";
|
||||
slides.forEach(function (slide) {
|
||||
Display.addTextSlide(slide.verse, slide.text, slide.footer, false);
|
||||
slide_html = Display._createTextSlide(slide.verse, slide.text);
|
||||
parentSection.appendChild(slide_html);
|
||||
Display._slides[slide.verse] = parentSection.children.length - 1;
|
||||
if (slide.footer) {
|
||||
Display._footerContainer.innerHTML = slide.footer;
|
||||
}
|
||||
});
|
||||
Display.reinit();
|
||||
Display.replaceSlides(parentSection, true);
|
||||
},
|
||||
/**
|
||||
* Create the <section> that will contain text slides (vertical slides in react)
|
||||
* Set a single text slide. This changes the slide with no transition.
|
||||
* Prevents the need to reapply the theme if only changing content.
|
||||
* @param String slide - Text to put on the slide
|
||||
*/
|
||||
_createTextContainer: function () {
|
||||
var slideContainer = document.createElement("section");
|
||||
slideContainer.classList = "text-slides";
|
||||
var slidesDiv = $(".slides")[0];
|
||||
slidesDiv.appendChild(slideContainer);
|
||||
// Apply the current theme to the new container
|
||||
if (!!Display._theme) {
|
||||
Display.setTheme(Display._theme);
|
||||
setTextSlide: function (text) {
|
||||
if (Display._slides.hasOwnProperty("test-slide") && Object.keys(Display._slides).length === 1) {
|
||||
var slide = $("#" + "test-slide")[0];
|
||||
var html = _prepareText(text);
|
||||
if (slide.innerHTML != html) {
|
||||
slide.innerHTML = html;
|
||||
}
|
||||
if (!Display._themeApplied) {
|
||||
Display.applyTheme(slide.parent);
|
||||
}
|
||||
} else {
|
||||
Display._clearSlidesList();
|
||||
var slide_html;
|
||||
var parentSection = document.createElement("section");
|
||||
parentSection.classList = "text-slides";
|
||||
slide_html = Display._createTextSlide("test-slide", text);
|
||||
parentSection.appendChild(slide_html);
|
||||
Display._slides["test-slide"] = 0;
|
||||
Display.applyTheme(parentSection);
|
||||
Display._slidesContainer.innerHTML = "";
|
||||
Display._slidesContainer.prepend(parentSection);
|
||||
Display.reinit();
|
||||
}
|
||||
},
|
||||
/**
|
||||
@ -695,8 +744,7 @@ var Display = {
|
||||
* @param {Object[]} slides - A list of images to add as JS objects [{"path": "url/to/file"}]
|
||||
*/
|
||||
setImageSlides: function (slides) {
|
||||
Display.clearSlides();
|
||||
var slidesDiv = $(".slides")[0];
|
||||
Display._clearSlidesList();
|
||||
var parentSection = document.createElement("section");
|
||||
slides.forEach(function (slide, index) {
|
||||
var section = document.createElement("section");
|
||||
@ -710,15 +758,14 @@ var Display = {
|
||||
parentSection.appendChild(section);
|
||||
Display._slides[index.toString()] = index;
|
||||
});
|
||||
slidesDiv.appendChild(parentSection);
|
||||
Display.reinit();
|
||||
Display.replaceSlides(parentSection);
|
||||
},
|
||||
/**
|
||||
* Set a video
|
||||
* @param {Object} video - The video to show as a JS object: {"path": "url/to/file"}
|
||||
*/
|
||||
setVideo: function (video) {
|
||||
Display.clearSlides();
|
||||
Display._clearSlidesList();
|
||||
var section = document.createElement("section");
|
||||
section.setAttribute("data-background", "#000");
|
||||
var videoElement = document.createElement("video");
|
||||
@ -747,8 +794,7 @@ var Display = {
|
||||
mediaWatcher.has_muted(event.target.muted);
|
||||
});
|
||||
section.appendChild(videoElement);
|
||||
$(".slides")[0].appendChild(section);
|
||||
Display.reinit();
|
||||
Display.replaceSlides(section);
|
||||
},
|
||||
/**
|
||||
* Play a video
|
||||
@ -856,32 +902,45 @@ var Display = {
|
||||
/**
|
||||
* Blank the screen
|
||||
*/
|
||||
blankToBlack: function () {
|
||||
toBlack: function () {
|
||||
var documentBody = $("body")[0];
|
||||
documentBody.style.opacity = 1;
|
||||
if (!Reveal.isPaused()) {
|
||||
Reveal.togglePause();
|
||||
}
|
||||
// var slidesDiv = $(".slides")[0];
|
||||
},
|
||||
/**
|
||||
* Blank to theme
|
||||
* Hide all but theme background
|
||||
*/
|
||||
blankToTheme: function () {
|
||||
var slidesDiv = $(".slides")[0];
|
||||
slidesDiv.style.visibility = "hidden";
|
||||
var footerDiv = $(".footer")[0];
|
||||
footerDiv.style.visibility = "hidden";
|
||||
toTheme: function () {
|
||||
var documentBody = $("body")[0];
|
||||
documentBody.style.opacity = 1;
|
||||
Display._slidesContainer.style.opacity = 0;
|
||||
Display._footerContainer.style.opacity = 0;
|
||||
if (Reveal.isPaused()) {
|
||||
Reveal.togglePause();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Hide everything (CAUTION: Causes a invisible mouse barrier)
|
||||
*/
|
||||
toTransparent: function () {
|
||||
Display._slidesContainer.style.opacity = 0;
|
||||
Display._footerContainer.style.opacity = 0;
|
||||
var documentBody = $("body")[0];
|
||||
documentBody.style.opacity = 0;
|
||||
if (!Reveal.isPaused()) {
|
||||
Reveal.togglePause();
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Show the screen
|
||||
*/
|
||||
show: function () {
|
||||
var slidesDiv = $(".slides")[0];
|
||||
slidesDiv.style.visibility = "visible";
|
||||
var footerDiv = $(".footer")[0];
|
||||
footerDiv.style.visibility = "visible";
|
||||
var documentBody = $("body")[0];
|
||||
documentBody.style.opacity = 1;
|
||||
Display._slidesContainer.style.opacity = 1;
|
||||
Display._footerContainer.style.opacity = 1;
|
||||
if (Reveal.isPaused()) {
|
||||
Reveal.togglePause();
|
||||
}
|
||||
@ -903,13 +962,31 @@ var Display = {
|
||||
var dh = parseFloat(_getStyle(d, "height"));
|
||||
return Math.floor(dh / lh);
|
||||
},
|
||||
/**
|
||||
* Prepare the theme for the next item to be added
|
||||
* @param theme The theme to be used
|
||||
*/
|
||||
setTheme: function (theme) {
|
||||
if (Display._theme != theme) {
|
||||
Display._themeApplied = false;
|
||||
Display._theme = theme;
|
||||
}
|
||||
},
|
||||
/**
|
||||
* Apply the theme to the provided element
|
||||
* @param targetElement The target element to apply the theme (expected to be a <section> in the slides container)
|
||||
* @param is_text Used to decide if the main area constraints should be applied
|
||||
*/
|
||||
applyTheme: function (targetElement, is_text=true) {
|
||||
Display._themeApplied = true;
|
||||
if (!Display._theme) {
|
||||
return;
|
||||
}
|
||||
// Set slide transitions
|
||||
var new_transition_type = "none",
|
||||
new_transition_speed = "default";
|
||||
if (!!theme.display_slide_transition && Display._doTransitions) {
|
||||
switch (theme.display_slide_transition_type) {
|
||||
if (!!Display._theme.display_slide_transition && Display._doTransitions) {
|
||||
switch (Display._theme.display_slide_transition_type) {
|
||||
case TransitionType.Fade:
|
||||
new_transition_type = "fade";
|
||||
break;
|
||||
@ -928,7 +1005,7 @@ var Display = {
|
||||
default:
|
||||
new_transition_type = "fade";
|
||||
}
|
||||
switch (theme.display_slide_transition_speed) {
|
||||
switch (Display._theme.display_slide_transition_speed) {
|
||||
case TransitionSpeed.Normal:
|
||||
new_transition_speed = "default";
|
||||
break;
|
||||
@ -941,7 +1018,7 @@ var Display = {
|
||||
default:
|
||||
new_transition_speed = "default";
|
||||
}
|
||||
switch (theme.display_slide_transition_direction) {
|
||||
switch (Display._theme.display_slide_transition_direction) {
|
||||
case TransitionDirection.Vertical:
|
||||
new_transition_type += "-vertical";
|
||||
break;
|
||||
@ -949,95 +1026,102 @@ var Display = {
|
||||
default:
|
||||
new_transition_type += "-horizontal";
|
||||
}
|
||||
if (theme.display_slide_transition_reverse) {
|
||||
if (Display._theme.display_slide_transition_reverse) {
|
||||
new_transition_type += "-reverse";
|
||||
}
|
||||
}
|
||||
|
||||
Display.setTransition(new_transition_type, new_transition_speed);
|
||||
var slides = targetElement.children;
|
||||
for (var i = 0; i < slides.length; i++) {
|
||||
slides[i].setAttribute("data-transition", new_transition_type);
|
||||
slides[i].setAttribute("data-transition-speed", new_transition_speed);
|
||||
}
|
||||
// Set the background
|
||||
var globalBackground = $("#global-background")[0];
|
||||
var backgroundStyle = {};
|
||||
var backgroundContent = "";
|
||||
var backgroundHtml = "";
|
||||
switch (theme.background_type) {
|
||||
var globalBackground = $("#global-background")[0];
|
||||
globalBackground.style.setProperty("background", "black");
|
||||
switch (Display._theme.background_type) {
|
||||
case BackgroundType.Transparent:
|
||||
backgroundStyle.background = "transparent";
|
||||
backgroundContent = "transparent";
|
||||
globalBackground.style.setProperty("background", "transparent");
|
||||
break;
|
||||
case BackgroundType.Solid:
|
||||
backgroundStyle.background = theme.background_color;
|
||||
backgroundContent = Display._theme.background_color;
|
||||
break;
|
||||
case BackgroundType.Gradient:
|
||||
switch (theme.background_direction) {
|
||||
switch (Display._theme.background_direction) {
|
||||
case GradientType.Horizontal:
|
||||
backgroundStyle.background = _buildLinearGradient("left top", "left bottom",
|
||||
theme.background_start_color,
|
||||
theme.background_end_color);
|
||||
backgroundContent = _buildLinearGradient("left top", "left bottom",
|
||||
Display._theme.background_start_color,
|
||||
Display._theme.background_end_color);
|
||||
break;
|
||||
case GradientType.Vertical:
|
||||
backgroundStyle.background = _buildLinearGradient("left top", "right top",
|
||||
theme.background_start_color,
|
||||
theme.background_end_color);
|
||||
backgroundContent = _buildLinearGradient("left top", "right top",
|
||||
Display._theme.background_start_color,
|
||||
Display._theme.background_end_color);
|
||||
break;
|
||||
case GradientType.LeftTop:
|
||||
backgroundStyle.background = _buildLinearGradient("left top", "right bottom",
|
||||
theme.background_start_color,
|
||||
theme.background_end_color);
|
||||
backgroundContent = _buildLinearGradient("left top", "right bottom",
|
||||
Display._theme.background_start_color,
|
||||
Display._theme.background_end_color);
|
||||
break;
|
||||
case GradientType.LeftBottom:
|
||||
backgroundStyle.background = _buildLinearGradient("left bottom", "right top",
|
||||
theme.background_start_color,
|
||||
theme.background_end_color);
|
||||
backgroundContent = _buildLinearGradient("left bottom", "right top",
|
||||
Display._theme.background_start_color,
|
||||
Display._theme.background_end_color);
|
||||
break;
|
||||
case GradientType.Circular:
|
||||
backgroundStyle.background = _buildRadialGradient(window.innerWidth / 2, theme.background_start_color,
|
||||
theme.background_end_color);
|
||||
backgroundContent = _buildRadialGradient(window.innerWidth / 2, Display._theme.background_start_color,
|
||||
Display._theme.background_end_color);
|
||||
break;
|
||||
default:
|
||||
backgroundStyle.background = "#000";
|
||||
backgroundContent = "#000";
|
||||
}
|
||||
break;
|
||||
case BackgroundType.Image:
|
||||
backgroundStyle["background-image"] = "url('" + theme.background_filename + "')";
|
||||
console.warn(backgroundStyle["background-image"]);
|
||||
backgroundContent = "url('" + Display._theme.background_filename + "')";
|
||||
console.warn(backgroundContent);
|
||||
break;
|
||||
case BackgroundType.Video:
|
||||
// never actually used since background type is overridden from video to transparent in window.py
|
||||
backgroundStyle["background-color"] = theme.background_border_color;
|
||||
backgroundHtml = "<video loop autoplay muted><source src='" + theme.background_filename + "'></video>";
|
||||
backgroundContent = Display._theme.background_border_color;
|
||||
backgroundHtml = "<video loop autoplay muted><source src='" + Display._theme.background_filename + "'></video>";
|
||||
console.warn(backgroundHtml);
|
||||
break;
|
||||
default:
|
||||
backgroundStyle.background = "#000";
|
||||
}
|
||||
globalBackground.style.cssText = "";
|
||||
for (var bgKey in backgroundStyle) {
|
||||
if (backgroundStyle.hasOwnProperty(bgKey)) {
|
||||
globalBackground.style.setProperty(bgKey, backgroundStyle[bgKey]);
|
||||
}
|
||||
backgroundContent = "#000";
|
||||
}
|
||||
targetElement.style.cssText = "";
|
||||
targetElement.setAttribute("data-background", backgroundContent);
|
||||
targetElement.setAttribute("data-background-size", "cover");
|
||||
if (!!backgroundHtml) {
|
||||
globalBackground.innerHTML = backgroundHtml;
|
||||
background.innerHTML = backgroundHtml;
|
||||
}
|
||||
|
||||
// set up the main area
|
||||
if (!is_text) {
|
||||
// only transition and background for non text slides
|
||||
return;
|
||||
}
|
||||
mainStyle = {};
|
||||
if (!!theme.font_main_outline) {
|
||||
mainStyle["-webkit-text-stroke"] = "" + theme.font_main_outline_size + "pt " +
|
||||
theme.font_main_outline_color;
|
||||
mainStyle["-webkit-text-fill-color"] = theme.font_main_color;
|
||||
if (!!Display._theme.font_main_outline) {
|
||||
mainStyle["-webkit-text-stroke"] = "" + Display._theme.font_main_outline_size + "pt " +
|
||||
Display._theme.font_main_outline_color;
|
||||
mainStyle["-webkit-text-fill-color"] = Display._theme.font_main_color;
|
||||
}
|
||||
// These need to be fixed, in the Python they use a width passed in as a parameter
|
||||
mainStyle.width = theme.font_main_width + "px";
|
||||
mainStyle.height = theme.font_main_height + "px";
|
||||
mainStyle["margin-top"] = "" + theme.font_main_y + "px";
|
||||
mainStyle.left = "" + theme.font_main_x + "px";
|
||||
mainStyle.color = theme.font_main_color;
|
||||
mainStyle["font-family"] = theme.font_main_name;
|
||||
mainStyle["font-size"] = "" + theme.font_main_size + "pt";
|
||||
mainStyle["font-style"] = !!theme.font_main_italics ? "italic" : "";
|
||||
mainStyle["font-weight"] = !!theme.font_main_bold ? "bold" : "";
|
||||
mainStyle["line-height"] = "" + (100 + theme.font_main_line_adjustment) + "%";
|
||||
mainStyle.width = Display._theme.font_main_width + "px";
|
||||
mainStyle.height = Display._theme.font_main_height + "px";
|
||||
mainStyle["margin-top"] = "" + Display._theme.font_main_y + "px";
|
||||
mainStyle.left = "" + Display._theme.font_main_x + "px";
|
||||
mainStyle.color = Display._theme.font_main_color;
|
||||
mainStyle["font-family"] = Display._theme.font_main_name;
|
||||
mainStyle["font-size"] = "" + Display._theme.font_main_size + "pt";
|
||||
mainStyle["font-style"] = !!Display._theme.font_main_italics ? "italic" : "";
|
||||
mainStyle["font-weight"] = !!Display._theme.font_main_bold ? "bold" : "";
|
||||
mainStyle["line-height"] = "" + (100 + Display._theme.font_main_line_adjustment) + "%";
|
||||
// Using text-align-last because there is a <br> seperating each line
|
||||
switch (theme.display_horizontal_align) {
|
||||
switch (Display._theme.display_horizontal_align) {
|
||||
case HorizontalAlign.Justify:
|
||||
mainStyle["text-align"] = "justify";
|
||||
mainStyle["text-align-last"] = "justify";
|
||||
@ -1058,7 +1142,7 @@ var Display = {
|
||||
mainStyle["text-align"] = "center";
|
||||
mainStyle["text-align-last"] = "center";
|
||||
}
|
||||
switch (theme.display_vertical_align) {
|
||||
switch (Display._theme.display_vertical_align) {
|
||||
case VerticalAlign.Middle:
|
||||
mainStyle["justify-content"] = "center";
|
||||
break;
|
||||
@ -1068,23 +1152,19 @@ var Display = {
|
||||
case VerticalAlign.Bottom:
|
||||
mainStyle["justify-content"] = "flex-end";
|
||||
// This gets around the webkit scroll height bug
|
||||
mainStyle["padding-bottom"] = "" + (theme.font_main_size / 8) + "px";
|
||||
mainStyle["padding-bottom"] = "" + (Display._theme.font_main_size / 8) + "px";
|
||||
break;
|
||||
default:
|
||||
mainStyle["justify-content"] = "center";
|
||||
}
|
||||
if (theme.hasOwnProperty('font_main_shadow_size') && !!theme.font_main_shadow) {
|
||||
mainStyle["text-shadow"] = theme.font_main_shadow_color + " " + theme.font_main_shadow_size + "pt " +
|
||||
theme.font_main_shadow_size + "pt";
|
||||
if (Display._theme.hasOwnProperty('font_main_shadow_size') && !!Display._theme.font_main_shadow) {
|
||||
mainStyle["text-shadow"] = Display._theme.font_main_shadow_color + " " + Display._theme.font_main_shadow_size + "pt " +
|
||||
Display._theme.font_main_shadow_size + "pt";
|
||||
}
|
||||
var slidesDiv = $("section.text-slides");
|
||||
if (slidesDiv.length > 0) {
|
||||
slidesDiv = slidesDiv[0];
|
||||
slidesDiv.style.cssText = "";
|
||||
targetElement.style.cssText = "";
|
||||
for (var mainKey in mainStyle) {
|
||||
if (mainStyle.hasOwnProperty(mainKey)) {
|
||||
slidesDiv.style.setProperty(mainKey, mainStyle[mainKey]);
|
||||
}
|
||||
targetElement.style.setProperty(mainKey, mainStyle[mainKey]);
|
||||
}
|
||||
}
|
||||
// Set up the footer
|
||||
@ -1092,19 +1172,18 @@ var Display = {
|
||||
"text-align": "left"
|
||||
};
|
||||
footerStyle.position = "absolute";
|
||||
footerStyle.left = "" + theme.font_footer_x + "px";
|
||||
footerStyle.top = "" + theme.font_footer_y + "px";
|
||||
footerStyle.width = "" + theme.font_footer_width + "px";
|
||||
footerStyle.height = "" + theme.font_footer_height + "px";
|
||||
footerStyle.color = theme.font_footer_color;
|
||||
footerStyle["font-family"] = theme.font_footer_name;
|
||||
footerStyle["font-size"] = "" + theme.font_footer_size + "pt";
|
||||
footerStyle["white-space"] = theme.font_footer_wrap ? "normal" : "nowrap";
|
||||
var footer = $(".footer")[0];
|
||||
footer.style.cssText = "";
|
||||
footerStyle.left = "" + Display._theme.font_footer_x + "px";
|
||||
footerStyle.top = "" + Display._theme.font_footer_y + "px";
|
||||
footerStyle.width = "" + Display._theme.font_footer_width + "px";
|
||||
footerStyle.height = "" + Display._theme.font_footer_height + "px";
|
||||
footerStyle.color = Display._theme.font_footer_color;
|
||||
footerStyle["font-family"] = Display._theme.font_footer_name;
|
||||
footerStyle["font-size"] = "" + Display._theme.font_footer_size + "pt";
|
||||
footerStyle["white-space"] = Display._theme.font_footer_wrap ? "normal" : "nowrap";
|
||||
Display._footerContainer.style.cssText = "";
|
||||
for (var footerKey in footerStyle) {
|
||||
if (footerStyle.hasOwnProperty(footerKey)) {
|
||||
footer.style.setProperty(footerKey, footerStyle[footerKey]);
|
||||
Display._footerContainer.style.setProperty(footerKey, footerStyle[footerKey]);
|
||||
}
|
||||
}
|
||||
},
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
|
@ -41,6 +41,7 @@ class HideMode(object):
|
||||
Blank = 1
|
||||
Theme = 2
|
||||
Screen = 3
|
||||
Transparent = 4
|
||||
|
||||
|
||||
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.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]
|
||||
|
@ -855,7 +855,7 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
||||
self.preview_display.set_theme(theme_data, service_item_type=service_item.service_item_type)
|
||||
# Set theme for displays
|
||||
for display in self.displays:
|
||||
display.set_theme(service_item.get_theme_data(), service_item_type=service_item.service_item_type)
|
||||
display.set_theme(theme_data, service_item_type=service_item.service_item_type)
|
||||
|
||||
def _process_item(self, service_item, slide_no):
|
||||
"""
|
||||
@ -1168,20 +1168,9 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
||||
Registry().execute('{text}_slide'.format(text=self.service_item.name.lower()),
|
||||
[self.service_item, self.is_live, row])
|
||||
else:
|
||||
# to_display = self.service_item.get_rendered_frame(row)
|
||||
if self.service_item.is_text():
|
||||
for display in self.displays:
|
||||
display.go_to_slide(row)
|
||||
# self.display.text(to_display, row != old_selected_row)
|
||||
else:
|
||||
if start:
|
||||
for display in self.displays:
|
||||
display.load_images(self.service_item.slides)
|
||||
# self.display.build_html(self.service_item, to_display)
|
||||
else:
|
||||
for display in self.displays:
|
||||
display.go_to_slide(row)
|
||||
# self.display.image(to_display)
|
||||
if not self.service_item.is_text():
|
||||
# reset the store used to display first image
|
||||
self.service_item.bg_image_bytes = None
|
||||
self.selected_row = row
|
||||
|
@ -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:
|
||||
|
@ -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
|
||||
|
@ -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()
|
||||
|
@ -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
|
||||
|
@ -76,6 +76,7 @@ describe("The function", function () {
|
||||
});
|
||||
|
||||
describe("The Display object", function () {
|
||||
|
||||
it("should start with a blank _slides object", function () {
|
||||
expect(Display._slides).toEqual({});
|
||||
});
|
||||
@ -107,6 +108,8 @@ describe("The Display object", function () {
|
||||
|
||||
it("should initialise Reveal when init is called", function () {
|
||||
spyOn(Reveal, "initialize");
|
||||
document.body.innerHTML = "";
|
||||
_createDiv({"id": "global-background"});
|
||||
Display.init();
|
||||
expect(Reveal.initialize).toHaveBeenCalled();
|
||||
});
|
||||
@ -123,16 +126,18 @@ describe("The Display object", function () {
|
||||
expect(Reveal.slide).toHaveBeenCalledWith(0);
|
||||
});
|
||||
|
||||
it("should have a setTransition() method", function () {
|
||||
expect(Display.setTransition).toBeDefined();
|
||||
it("should have a setItemTransition() method", function () {
|
||||
expect(Display.setItemTransition).toBeDefined();
|
||||
});
|
||||
|
||||
it("should have a correctly functioning clearSlides() method", function () {
|
||||
expect(Display.clearSlides).toBeDefined();
|
||||
|
||||
document.body.innerHTML = "";
|
||||
var slidesDiv = _createDiv({"class": "slides"});
|
||||
slidesDiv.innerHTML = "<section><p></p></section>";
|
||||
Display._slidesContainer = slidesDiv;
|
||||
var footerDiv = _createDiv({"class": "footer"});
|
||||
Display._footerContainer = footerDiv;
|
||||
|
||||
Display.clearSlides();
|
||||
expect($(".slides")[0].innerHTML).toEqual("");
|
||||
@ -167,42 +172,48 @@ describe("Transitions", function () {
|
||||
Display._theme = null;
|
||||
});
|
||||
|
||||
it("should have a correctly functioning setTransition() method", function () {
|
||||
it("should have a correctly functioning setItemTransition() method", function () {
|
||||
spyOn(Reveal, "configure");
|
||||
Display.setTransition("fade", "slow");
|
||||
expect(Reveal.configure).toHaveBeenCalledWith({"transition": "fade", "transitionSpeed": "slow"});
|
||||
Display.setItemTransition(true);
|
||||
expect(Reveal.configure).toHaveBeenCalledWith({"backgroundTransition": "fade", "transitionSpeed": "default"});
|
||||
});
|
||||
|
||||
it("should have enabled transitions when _doTransitions is true and setTheme is run", function () {
|
||||
spyOn(Display, "setTransition");
|
||||
it("should have enabled transitions when _doTransitions is true and applyTheme is run", function () {
|
||||
Display._doTransitions = true;
|
||||
var theme = {
|
||||
"display_slide_transition": true,
|
||||
"display_slide_transition_type": TransitionType.Slide,
|
||||
"display_slide_transition_speed": TransitionSpeed.Fast
|
||||
}
|
||||
|
||||
Display.setTheme(theme);
|
||||
var slidesDiv = _createDiv({"class": "slides"});
|
||||
slidesDiv.innerHTML = "<section><section><p></p></section></section>";
|
||||
Display._slidesContainer = slidesDiv;
|
||||
|
||||
expect(Display.setTransition).toHaveBeenCalledWith("slide-horizontal", "fast");
|
||||
Display.applyTheme(Display._slidesContainer.children[0])
|
||||
|
||||
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition")).toEqual("slide-horizontal");
|
||||
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition-speed")).toEqual("fast");
|
||||
});
|
||||
|
||||
it("should have not enabled transitions when init() with no transitions and setTheme is run", function () {
|
||||
spyOn(Display, "setTransition");
|
||||
Display._doTransitions = false;
|
||||
var theme = {
|
||||
"display_slide_transition": true,
|
||||
"display_slide_transition_type": TransitionType.Slide,
|
||||
"display_slide_transition_speed": TransitionSpeed.Fast,
|
||||
}
|
||||
|
||||
Display.setTheme(theme);
|
||||
var slidesDiv = _createDiv({"class": "slides"});
|
||||
slidesDiv.innerHTML = "<section><section><p></p></section></section>";
|
||||
Display._slidesContainer = slidesDiv;
|
||||
|
||||
expect(Display.setTransition).toHaveBeenCalledWith("none", "default");
|
||||
Display.applyTheme(Display._slidesContainer.children[0])
|
||||
|
||||
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition")).toEqual("none");
|
||||
});
|
||||
|
||||
it("should have enabled transitions in the correct direction", function () {
|
||||
spyOn(Display, "setTransition");
|
||||
Display._doTransitions = true;
|
||||
var theme = {
|
||||
"display_slide_transition": true,
|
||||
@ -211,10 +222,15 @@ describe("Transitions", function () {
|
||||
"display_slide_transition_direction": TransitionDirection.Vertical,
|
||||
"display_slide_transition_reverse": true,
|
||||
}
|
||||
|
||||
Display.setTheme(theme);
|
||||
var slidesDiv = _createDiv({"class": "slides"});
|
||||
slidesDiv.innerHTML = "<section><section><p></p></section></section>";
|
||||
Display._slidesContainer = slidesDiv;
|
||||
|
||||
expect(Display.setTransition).toHaveBeenCalledWith("convex-vertical-reverse", "slow");
|
||||
Display.applyTheme(Display._slidesContainer.children[0])
|
||||
|
||||
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition")).toEqual("convex-vertical-reverse");
|
||||
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition-speed")).toEqual("slow");
|
||||
});
|
||||
|
||||
});
|
||||
@ -504,63 +520,49 @@ describe("Display.alertAnimationEndEvent", function () {
|
||||
});
|
||||
});
|
||||
|
||||
describe("Display.addTextSlide", function () {
|
||||
describe("Display.setTextSlide", function () {
|
||||
beforeEach(function() {
|
||||
document.body.innerHTML = "";
|
||||
_createDiv({"class": "slides"});
|
||||
_createDiv({"class": "footer"});
|
||||
var slides_container = _createDiv({"class": "slides"});
|
||||
var footer_container = _createDiv({"class": "footer"});
|
||||
Display._slidesContainer = slides_container;
|
||||
Display._footerContainer = footer_container;
|
||||
Display._slides = {};
|
||||
});
|
||||
|
||||
it("should add a new slide", function () {
|
||||
var verse = "v1",
|
||||
text = "Amazing grace,\nhow sweet the sound",
|
||||
footer = "Public Domain";
|
||||
var text = "Amazing grace,\nhow sweet the sound";
|
||||
spyOn(Display, "reinit");
|
||||
|
||||
Display.addTextSlide(verse, text, footer);
|
||||
Display.setTextSlide(text);
|
||||
|
||||
expect(Display._slides[verse]).toEqual(0);
|
||||
expect(Display._slides["test-slide"]).toEqual(0);
|
||||
expect($(".slides > section > section").length).toEqual(1);
|
||||
expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
|
||||
expect(Display.reinit).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should add a new slide without calling reinit()", function () {
|
||||
var verse = "v1",
|
||||
text = "Amazing grace,\nhow sweet the sound",
|
||||
footer = "Public Domain";
|
||||
spyOn(Display, "reinit");
|
||||
|
||||
Display.addTextSlide(verse, text, footer, false);
|
||||
|
||||
expect(Display._slides[verse]).toEqual(0);
|
||||
expect($(".slides > section > section").length).toEqual(1);
|
||||
expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
|
||||
expect(Display.reinit).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("should update an existing slide", function () {
|
||||
var verse = "v1",
|
||||
text = "Amazing grace, how sweet the sound\nThat saved a wretch like me",
|
||||
footer = "Public Domain";
|
||||
Display.addTextSlide(verse, "Amazing grace,\nhow sweet the sound", footer, false);
|
||||
var text = "That saved a wretch\nlike me";
|
||||
spyOn(Display, "reinit");
|
||||
Display.setTextSlide("Amazing grace,\nhow sweet the sound");
|
||||
|
||||
Display.addTextSlide(verse, text, footer, true);
|
||||
Display.setTextSlide(text);
|
||||
|
||||
expect(Display._slides[verse]).toEqual(0);
|
||||
expect(Display._slides["test-slide"]).toEqual(0);
|
||||
expect($(".slides > section > section").length).toEqual(1);
|
||||
expect($(".slides > section > section")[0].innerHTML).toEqual(_prepareText(text));
|
||||
expect(Display.reinit).toHaveBeenCalledTimes(0);
|
||||
expect(Display.reinit).toHaveBeenCalledTimes(1); // only called once for the first setTextSlide
|
||||
});
|
||||
});
|
||||
|
||||
describe("Display.setTextSlides", function () {
|
||||
beforeEach(function() {
|
||||
document.body.innerHTML = "";
|
||||
_createDiv({"class": "slides"});
|
||||
_createDiv({"class": "footer"});
|
||||
var slides_container = _createDiv({"class": "slides"});
|
||||
var footer_container = _createDiv({"class": "footer"});
|
||||
Display._slidesContainer = slides_container;
|
||||
Display._footerContainer = footer_container;
|
||||
_createDiv({"id": "global-background"});
|
||||
Display._slides = {};
|
||||
});
|
||||
@ -581,15 +583,16 @@ describe("Display.setTextSlides", function () {
|
||||
}
|
||||
];
|
||||
spyOn(Display, "clearSlides");
|
||||
spyOn(Display, "reinit");
|
||||
spyOn(Reveal, "sync");
|
||||
spyOn(Reveal, "slide");
|
||||
|
||||
Display.setTextSlides(slides);
|
||||
|
||||
expect(Display.clearSlides).toHaveBeenCalledTimes(1);
|
||||
expect(Display.clearSlides).toHaveBeenCalledTimes(0);
|
||||
expect(Display._slides["v1"]).toEqual(0);
|
||||
expect(Display._slides["v2"]).toEqual(1);
|
||||
expect($(".slides > section > section").length).toEqual(2);
|
||||
expect(Display.reinit).toHaveBeenCalledTimes(1);
|
||||
expect(Reveal.sync).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it("should correctly set outline width", function () {
|
||||
@ -607,7 +610,7 @@ describe("Display.setTextSlides", function () {
|
||||
'font_main_outline_size': 42,
|
||||
'font_main_outline_color': 'red'
|
||||
};
|
||||
spyOn(Display, "reinit");
|
||||
spyOn(Reveal, "sync");
|
||||
spyOn(Reveal, "slide");
|
||||
|
||||
Display.setTheme(theme);
|
||||
@ -633,7 +636,7 @@ describe("Display.setTextSlides", function () {
|
||||
'display_horizontal_align': 3,
|
||||
'display_vertical_align': 1
|
||||
};
|
||||
spyOn(Display, "reinit");
|
||||
spyOn(Reveal, "sync");
|
||||
spyOn(Reveal, "slide");
|
||||
|
||||
Display.setTheme(theme);
|
||||
@ -659,7 +662,7 @@ describe("Display.setTextSlides", function () {
|
||||
'font_main_shadow_color': "#000",
|
||||
'font_main_shadow_size': 5
|
||||
};
|
||||
spyOn(Display, "reinit");
|
||||
spyOn(Reveal, "sync");
|
||||
spyOn(Reveal, "slide");
|
||||
|
||||
Display.setTheme(theme);
|
||||
@ -684,7 +687,7 @@ describe("Display.setTextSlides", function () {
|
||||
'font_main_shadow_color': "#000",
|
||||
'font_main_shadow_size': 5
|
||||
};
|
||||
spyOn(Display, "reinit");
|
||||
spyOn(Reveal, "sync");
|
||||
spyOn(Reveal, "slide");
|
||||
|
||||
Display.setTheme(theme);
|
||||
@ -710,7 +713,7 @@ describe("Display.setTextSlides", function () {
|
||||
'font_main_width': 1230,
|
||||
'font_main_height': 4560
|
||||
};
|
||||
spyOn(Display, "reinit");
|
||||
spyOn(Reveal, "sync");
|
||||
spyOn(Reveal, "slide");
|
||||
|
||||
Display.setTheme(theme);
|
||||
@ -727,20 +730,21 @@ describe("Display.setTextSlides", function () {
|
||||
describe("Display.setImageSlides", function () {
|
||||
beforeEach(function() {
|
||||
document.body.innerHTML = "";
|
||||
_createDiv({"class": "slides"});
|
||||
_createDiv({"class": "footer"});
|
||||
var slides_container = _createDiv({"class": "slides"});
|
||||
var footer_container = _createDiv({"class": "footer"});
|
||||
Display._slidesContainer = slides_container;
|
||||
Display._footerContainer = footer_container;
|
||||
_createDiv({"id": "global-background"});
|
||||
Display._slides = {};
|
||||
});
|
||||
|
||||
it("should add a list of images", function () {
|
||||
var slides = [{"path": "file:///openlp1.jpg"}, {"path": "file:///openlp2.jpg"}];
|
||||
spyOn(Display, "clearSlides");
|
||||
spyOn(Display, "reinit");
|
||||
spyOn(Reveal, "sync");
|
||||
spyOn(Reveal, "slide");
|
||||
|
||||
Display.setImageSlides(slides);
|
||||
|
||||
expect(Display.clearSlides).toHaveBeenCalledTimes(1);
|
||||
expect(Display._slides["0"]).toEqual(0);
|
||||
expect(Display._slides["1"]).toEqual(1);
|
||||
expect($(".slides > section > section").length).toEqual(2);
|
||||
@ -749,30 +753,30 @@ describe("Display.setImageSlides", function () {
|
||||
expect($(".slides > section > section > img")[0].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);")
|
||||
expect($(".slides > section > section > img")[1].getAttribute("src")).toEqual("file:///openlp2.jpg")
|
||||
expect($(".slides > section > section > img")[1].getAttribute("style")).toEqual("max-width: 100%; max-height: 100%; margin: 0; position: absolute; top: 50%; left: 50%; transform: translate(-50%, -50%);")
|
||||
expect(Display.reinit).toHaveBeenCalledTimes(1);
|
||||
expect(Reveal.sync).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Display.setVideo", function () {
|
||||
beforeEach(function() {
|
||||
document.body.innerHTML = "";
|
||||
_createDiv({"class": "slides"});
|
||||
var slides_container = _createDiv({"class": "slides"});
|
||||
Display._slidesContainer = slides_container;
|
||||
_createDiv({"id": "global-background"});
|
||||
Display._slides = {};
|
||||
});
|
||||
|
||||
it("should add a video to the page", function () {
|
||||
var video = {"path": "file:///video.mp4"};
|
||||
spyOn(Display, "clearSlides");
|
||||
spyOn(Display, "reinit");
|
||||
spyOn(Reveal, "sync");
|
||||
spyOn(Reveal, "slide");
|
||||
|
||||
Display.setVideo(video);
|
||||
|
||||
expect(Display.clearSlides).toHaveBeenCalledTimes(1);
|
||||
expect($(".slides > section").length).toEqual(1);
|
||||
expect($(".slides > section > video").length).toEqual(1);
|
||||
expect($(".slides > section > video")[0].src).toEqual("file:///video.mp4")
|
||||
expect(Display.reinit).toHaveBeenCalledTimes(1);
|
||||
expect(Reveal.sync).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
});
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user