forked from openlp/openlp
Merge branch 'previous-verse-flash-fix' into 'master'
Adjusting display transitions that causes content flashes Closes #979 See merge request openlp/openlp!429
This commit is contained in:
commit
4c83aa58c1
@ -46,6 +46,27 @@ body.transition .reveal .footer {
|
|||||||
transition: opacity 800ms ease-in-out !important;
|
transition: opacity 800ms ease-in-out !important;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
/*
|
||||||
|
It's a fake transition, but the transitionend event will be
|
||||||
|
fired at the end of the transition time. For the user there will
|
||||||
|
be no animation (due to the curve
|
||||||
|
*/
|
||||||
|
transition: opacity 75ms steps(1, start);
|
||||||
|
}
|
||||||
|
|
||||||
|
body.disable-transitions *,
|
||||||
|
body.disable-transitions.transition,
|
||||||
|
body.disable-transitions.transition .reveal .slides,
|
||||||
|
body.disable-transitions.transition .reveal .footer {
|
||||||
|
transition: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
body.is-desktop .pause-overlay {
|
||||||
|
/* Avoids a black flash while activating "Show Desktop" */
|
||||||
|
opacity: 0 !important;
|
||||||
|
}
|
||||||
|
|
||||||
.reveal .slide-background {
|
.reveal .slide-background {
|
||||||
opacity: 1;
|
opacity: 1;
|
||||||
visibility: visible;
|
visibility: visible;
|
||||||
|
@ -321,6 +321,8 @@ var Display = {
|
|||||||
width: "100%",
|
width: "100%",
|
||||||
height: "100%"
|
height: "100%"
|
||||||
},
|
},
|
||||||
|
_lastRequestAnimationFrameHandle: null,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Start up reveal and do any other initialisation
|
* Start up reveal and do any other initialisation
|
||||||
* @param {object} options - The initialisation options:
|
* @param {object} options - The initialisation options:
|
||||||
@ -415,7 +417,7 @@ var Display = {
|
|||||||
Reveal.slide(0, currentSlide.v);
|
Reveal.slide(0, currentSlide.v);
|
||||||
Reveal.sync();
|
Reveal.sync();
|
||||||
Display._removeLastSection();
|
Display._removeLastSection();
|
||||||
Display._skipNextTransition = false;
|
Display._skipNextTransition = false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
@ -857,17 +859,37 @@ var Display = {
|
|||||||
/**
|
/**
|
||||||
* Blank the screen
|
* Blank the screen
|
||||||
*/
|
*/
|
||||||
toBlack: function () {
|
toBlack: function (onFinishedEventName) {
|
||||||
var documentBody = $("body")[0];
|
/* Avoid race conditions where display goes to transparent and quickly goes to black */
|
||||||
documentBody.style.opacity = 1;
|
Display._abortLastTransitionOperation();
|
||||||
if (!Reveal.isPaused()) {
|
/*
|
||||||
Reveal.togglePause();
|
Reveal's black overlay should be shown before the transitions are
|
||||||
}
|
restored, to avoid screen flashes
|
||||||
|
*/
|
||||||
|
Display._restorePauseBehavior();
|
||||||
|
Display._requestAnimationFrameExclusive(function() {
|
||||||
|
if (!Reveal.isPaused()) {
|
||||||
|
Reveal.togglePause();
|
||||||
|
}
|
||||||
|
Display._reenableGlobalTransitions(function() {
|
||||||
|
var documentBody = $("body")[0];
|
||||||
|
documentBody.style.opacity = 1;
|
||||||
|
if (onFinishedEventName) {
|
||||||
|
displayWatcher.dispatchEvent(onFinishedEventName, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Hide all but theme background
|
* Hide all but theme background
|
||||||
*/
|
*/
|
||||||
toTheme: function () {
|
toTheme: function (onFinishedEventName) {
|
||||||
|
Display._abortLastTransitionOperation();
|
||||||
|
/*
|
||||||
|
Reveal's black overlay should be shown before the transitions are
|
||||||
|
restored, to avoid screen flashes
|
||||||
|
*/
|
||||||
|
Display._restorePauseBehavior();
|
||||||
var documentBody = $("body")[0];
|
var documentBody = $("body")[0];
|
||||||
documentBody.style.opacity = 1;
|
documentBody.style.opacity = 1;
|
||||||
Display._slidesContainer.style.opacity = 0;
|
Display._slidesContainer.style.opacity = 0;
|
||||||
@ -875,31 +897,140 @@ var Display = {
|
|||||||
if (Reveal.isPaused()) {
|
if (Reveal.isPaused()) {
|
||||||
Reveal.togglePause();
|
Reveal.togglePause();
|
||||||
}
|
}
|
||||||
|
Display._reenableGlobalTransitions(function() {
|
||||||
|
if (onFinishedEventName) {
|
||||||
|
displayWatcher.dispatchEvent(onFinishedEventName, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Hide everything (CAUTION: Causes a invisible mouse barrier)
|
* Hide everything (CAUTION: Causes a invisible mouse barrier)
|
||||||
*/
|
*/
|
||||||
toTransparent: function () {
|
toTransparent: function (onFinishedEventName) {
|
||||||
Display._slidesContainer.style.opacity = 0;
|
Display._abortLastTransitionOperation();
|
||||||
Display._footerContainer.style.opacity = 0;
|
|
||||||
var documentBody = $("body")[0];
|
var documentBody = $("body")[0];
|
||||||
documentBody.style.opacity = 0;
|
documentBody.style.opacity = 0;
|
||||||
if (!Reveal.isPaused()) {
|
if (!Reveal.isPaused()) {
|
||||||
|
/*
|
||||||
|
Removing previously the overlay if it's not paused, to avoid a
|
||||||
|
content flash while going from black screen to transparent
|
||||||
|
*/
|
||||||
|
document.body.classList.add('is-desktop');
|
||||||
Reveal.togglePause();
|
Reveal.togglePause();
|
||||||
}
|
}
|
||||||
|
/*
|
||||||
|
Waiting for body transition to happen, now it would be safe to
|
||||||
|
hide the Webview (as other transitions were suppressed)
|
||||||
|
*/
|
||||||
|
Display._abortLastTransitionOperation();
|
||||||
|
Display._addTransitionEndEventToBody(transitionEndEvent);
|
||||||
|
function transitionEndEvent(e) {
|
||||||
|
// Targeting only body
|
||||||
|
if (e.target != documentBody) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
Disabling all transitions (except body) to allow the Webview to attain the
|
||||||
|
transparent state before it gets hidden by Qt.
|
||||||
|
*/
|
||||||
|
document.body.classList.add('disable-transitions');
|
||||||
|
document.body.classList.add('is-desktop');
|
||||||
|
Display._slidesContainer.style.opacity = 0;
|
||||||
|
Display._footerContainer.style.opacity = 0;
|
||||||
|
/*
|
||||||
|
Repainting before hiding the Webview to avoid flashes when
|
||||||
|
showing it again.
|
||||||
|
*/
|
||||||
|
displayWatcher.pleaseRepaint();
|
||||||
|
/* Waiting for repaint to happen before saying that it's done. */
|
||||||
|
Display._requestAnimationFrameExclusive(function() {
|
||||||
|
/* We're transparent now, aborting any transition event between */
|
||||||
|
Display._abortLastTransitionOperation();
|
||||||
|
if (onFinishedEventName) {
|
||||||
|
displayWatcher.dispatchEvent(onFinishedEventName, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Show the screen
|
* Show the screen
|
||||||
*/
|
*/
|
||||||
show: function () {
|
show: function (onFinishedEventName) {
|
||||||
var documentBody = $("body")[0];
|
var documentBody = $("body")[0];
|
||||||
documentBody.style.opacity = 1;
|
/*
|
||||||
|
Removing transitionend event, avoids the content being hidden if the user
|
||||||
|
tries to show content again before toTransparent() transitionend event
|
||||||
|
happens
|
||||||
|
*/
|
||||||
|
Display._abortLastTransitionOperation();
|
||||||
|
|
||||||
Display._slidesContainer.style.opacity = 1;
|
Display._slidesContainer.style.opacity = 1;
|
||||||
Display._footerContainer.style.opacity = 1;
|
Display._footerContainer.style.opacity = 1;
|
||||||
if (Reveal.isPaused()) {
|
if (Reveal.isPaused()) {
|
||||||
Reveal.togglePause();
|
Reveal.togglePause();
|
||||||
}
|
}
|
||||||
|
Display._restorePauseBehavior();
|
||||||
|
Display._reenableGlobalTransitions(function() {
|
||||||
|
documentBody.style.opacity = 1;
|
||||||
|
if (onFinishedEventName) {
|
||||||
|
displayWatcher.dispatchEvent(onFinishedEventName, {});
|
||||||
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
_reenableGlobalTransitions: function(afterCallback) {
|
||||||
|
Display._requestAnimationFrameExclusive(function() {
|
||||||
|
/*
|
||||||
|
Waiting for the previous opacity + unpause operations to complete
|
||||||
|
to restore the transitions behavior
|
||||||
|
*/
|
||||||
|
document.body.classList.remove('disable-transitions');
|
||||||
|
if (typeof afterCallback === 'function') {
|
||||||
|
afterCallback();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Shows again the Reveal's black pause overlay that was
|
||||||
|
* hidden before Webview was hidden
|
||||||
|
*/
|
||||||
|
_restorePauseBehavior: function() {
|
||||||
|
document.body.classList.remove('is-desktop');
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Cancels previous requested animationFrame.
|
||||||
|
* Last animationFrame should be aborted to avoid race condition bugs when
|
||||||
|
* the user changes the view modes too quickly, for example.
|
||||||
|
*/
|
||||||
|
_requestAnimationFrameExclusive: function(callback) {
|
||||||
|
cancelAnimationFrame(Display._lastRequestAnimationFrameHandle);
|
||||||
|
Display._lastRequestAnimationFrameHandle = requestAnimationFrame(callback);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Aborts last body's transitionend and requestAnimationFrame's events, to avoid
|
||||||
|
* race condition bugs.
|
||||||
|
*/
|
||||||
|
_abortLastTransitionOperation: function() {
|
||||||
|
Display._removeTransitionEndEventToBody();
|
||||||
|
cancelAnimationFrame(Display._lastRequestAnimationFrameHandle);
|
||||||
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Intercepts the addEventListener call and stores it, so that it acts
|
||||||
|
* like the ontransitionend GlobalEventHandler.
|
||||||
|
*/
|
||||||
|
_addTransitionEndEventToBody: function(listener) {
|
||||||
|
Display._lastTransitionEndBodyEvent = listener;
|
||||||
|
document.body.addEventListener('transitionend', listener);
|
||||||
|
},
|
||||||
|
|
||||||
|
_removeTransitionEndEventToBody: function() {
|
||||||
|
document.body.removeEventListener('transitionend', Display._lastTransitionEndBodyEvent);
|
||||||
|
},
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Figure out how many lines can fit on a slide given the font size
|
* Figure out how many lines can fit on a slide given the font size
|
||||||
* @param fontSize The font size in pts
|
* @param fontSize The font size in pts
|
||||||
@ -1171,11 +1302,11 @@ var Display = {
|
|||||||
* and we don't want any flashbacks to the current slide contents
|
* and we don't want any flashbacks to the current slide contents
|
||||||
*/
|
*/
|
||||||
finishWithCurrentItem: function () {
|
finishWithCurrentItem: function () {
|
||||||
Display.setTextSlide('');
|
Display.setTextSlide('');
|
||||||
var documentBody = $("body")[0];
|
var documentBody = $("body")[0];
|
||||||
documentBody.style.opacity = 1;
|
documentBody.style.opacity = 1;
|
||||||
Display._skipNextTransition = true;
|
Display._skipNextTransition = true;
|
||||||
displayWatcher.pleaseRepaint();
|
displayWatcher.pleaseRepaint();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Return the video types supported by the video tag
|
* Return the video types supported by the video tag
|
||||||
@ -1227,7 +1358,7 @@ var Display = {
|
|||||||
*/
|
*/
|
||||||
setFooterSlideNumbers: function (slide) {
|
setFooterSlideNumbers: function (slide) {
|
||||||
let value = ['', '', ''];
|
let value = ['', '', ''];
|
||||||
// Reveal does call this function passing undefined
|
// Reveal does call this function passing undefined
|
||||||
if (typeof slide === 'undefined') {
|
if (typeof slide === 'undefined') {
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
@ -42,6 +42,7 @@ from openlp.core.ui import HideMode
|
|||||||
|
|
||||||
|
|
||||||
FONT_FOUNDRY = re.compile(r'(.*?) \[(.*?)\]')
|
FONT_FOUNDRY = re.compile(r'(.*?) \[(.*?)\]')
|
||||||
|
TRANSITION_END_EVENT_NAME = 'transparent_transition_end'
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
@ -54,6 +55,9 @@ class DisplayWatcher(QtCore.QObject):
|
|||||||
def __init__(self, parent):
|
def __init__(self, parent):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._display_window = parent
|
self._display_window = parent
|
||||||
|
self._transient_dispatch_events = {}
|
||||||
|
self._permanent_dispatch_events = {}
|
||||||
|
self._event_counter = 0
|
||||||
|
|
||||||
@QtCore.pyqtSlot(bool)
|
@QtCore.pyqtSlot(bool)
|
||||||
def setInitialised(self, is_initialised):
|
def setInitialised(self, is_initialised):
|
||||||
@ -70,6 +74,57 @@ class DisplayWatcher(QtCore.QObject):
|
|||||||
"""
|
"""
|
||||||
self._display_window.webview.update()
|
self._display_window.webview.update()
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(str, 'QJsonObject')
|
||||||
|
def dispatchEvent(self, event_name, event_data):
|
||||||
|
"""
|
||||||
|
Called from the js in the webengine view for event dispatches
|
||||||
|
"""
|
||||||
|
transient_dispatch_events = self._transient_dispatch_events
|
||||||
|
permanent_dispatch_events = self._permanent_dispatch_events
|
||||||
|
if event_name in transient_dispatch_events:
|
||||||
|
event = transient_dispatch_events[event_name]
|
||||||
|
del transient_dispatch_events[event_name]
|
||||||
|
event(event_data)
|
||||||
|
if event_name in permanent_dispatch_events:
|
||||||
|
permanent_dispatch_events[event_name](event_data)
|
||||||
|
|
||||||
|
def register_event_listener(self, event_name, callback, transient=True):
|
||||||
|
"""
|
||||||
|
Register an event listener from webengine view
|
||||||
|
:param event_name: Event name
|
||||||
|
:param callback: Callback listener when event happens
|
||||||
|
:param transient: If the event listener should be unregistered after being run
|
||||||
|
"""
|
||||||
|
if transient:
|
||||||
|
events = self._transient_dispatch_events
|
||||||
|
else:
|
||||||
|
events = self._permanent_dispatch_events
|
||||||
|
|
||||||
|
events[event_name] = callback
|
||||||
|
|
||||||
|
def unregister_event_listener(self, event_name, transient=True):
|
||||||
|
"""
|
||||||
|
Unregisters an event listener from webengine view
|
||||||
|
:param event_name: Event name
|
||||||
|
:param transient: If the event listener was registered as transient
|
||||||
|
"""
|
||||||
|
if transient:
|
||||||
|
events = self._transient_dispatch_events
|
||||||
|
else:
|
||||||
|
events = self._permanent_dispatch_events
|
||||||
|
|
||||||
|
if event_name in events:
|
||||||
|
del events[event_name]
|
||||||
|
|
||||||
|
def get_unique_event_name(self):
|
||||||
|
"""
|
||||||
|
Generates an unique event name
|
||||||
|
:returns: Unique event name
|
||||||
|
"""
|
||||||
|
event_count = self._event_counter
|
||||||
|
self._event_counter += 1
|
||||||
|
return 'event_' + str(event_count)
|
||||||
|
|
||||||
|
|
||||||
class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
"""
|
"""
|
||||||
@ -431,9 +486,11 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
|||||||
# Only make visible on single monitor setup if setting enabled.
|
# Only make visible on single monitor setup if setting enabled.
|
||||||
if len(ScreenList()) == 1 and not self.settings.value('core/display on monitor'):
|
if len(ScreenList()) == 1 and not self.settings.value('core/display on monitor'):
|
||||||
return
|
return
|
||||||
self.run_javascript('Display.show();')
|
# Aborting setVisible(False) call in case the display modes are changed quickly
|
||||||
|
self.display_watcher.unregister_event_listener(TRANSITION_END_EVENT_NAME)
|
||||||
if self.isHidden():
|
if self.isHidden():
|
||||||
self.setVisible(True)
|
self.setVisible(True)
|
||||||
|
self.run_javascript('Display.show();')
|
||||||
self.hide_mode = None
|
self.hide_mode = None
|
||||||
|
|
||||||
def hide_display(self, mode=HideMode.Screen):
|
def hide_display(self, mode=HideMode.Screen):
|
||||||
@ -447,19 +504,24 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
|||||||
# Only make visible on single monitor setup if setting enabled.
|
# Only make visible on single monitor setup if setting enabled.
|
||||||
if len(ScreenList()) == 1 and not self.settings.value('core/display on monitor'):
|
if len(ScreenList()) == 1 and not self.settings.value('core/display on monitor'):
|
||||||
return
|
return
|
||||||
|
# Aborting setVisible(False) call in case the display modes are changed quickly
|
||||||
|
self.display_watcher.unregister_event_listener(TRANSITION_END_EVENT_NAME)
|
||||||
# Update display to the selected mode
|
# Update display to the selected mode
|
||||||
|
if mode != HideMode.Screen:
|
||||||
|
if self.isHidden():
|
||||||
|
self.setVisible(True)
|
||||||
if mode == HideMode.Screen:
|
if mode == HideMode.Screen:
|
||||||
if self.settings.value('advanced/disable transparent display'):
|
if self.settings.value('advanced/disable transparent display'):
|
||||||
self.setVisible(False)
|
# Hide window only after all webview CSS ransitions are done
|
||||||
|
self.display_watcher.register_event_listener(TRANSITION_END_EVENT_NAME,
|
||||||
|
lambda _: self.setVisible(False))
|
||||||
|
self.run_javascript("Display.toTransparent('{}');".format(TRANSITION_END_EVENT_NAME))
|
||||||
else:
|
else:
|
||||||
self.run_javascript('Display.toTransparent();')
|
self.run_javascript('Display.toTransparent();')
|
||||||
elif mode == HideMode.Blank:
|
elif mode == HideMode.Blank:
|
||||||
self.run_javascript('Display.toBlack();')
|
self.run_javascript('Display.toBlack();')
|
||||||
elif mode == HideMode.Theme:
|
elif mode == HideMode.Theme:
|
||||||
self.run_javascript('Display.toTheme();')
|
self.run_javascript('Display.toTheme();')
|
||||||
if mode != HideMode.Screen:
|
|
||||||
if self.isHidden():
|
|
||||||
self.setVisible(True)
|
|
||||||
self.hide_mode = mode
|
self.hide_mode = mode
|
||||||
|
|
||||||
def disable_display(self):
|
def disable_display(self):
|
||||||
|
@ -308,7 +308,92 @@ describe("Transitions", function () {
|
|||||||
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition")).toEqual("convex-vertical-reverse");
|
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");
|
expect(Display._slidesContainer.children[0].children[0].getAttribute("data-transition-speed")).toEqual("slow");
|
||||||
});
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
describe("Screen Visibility", function () {
|
||||||
|
var TRANSITION_TIMEOUT = 2000;
|
||||||
|
|
||||||
|
beforeEach(function() {
|
||||||
|
window.displayWatcher = jasmine.createSpyObj('DisplayWatcher', ['dispatchEvent', 'setInitialised', 'pleaseRepaint']);
|
||||||
|
document.body.innerHTML = "";
|
||||||
|
var revealDiv = _createDiv({"class": "reveal"});
|
||||||
|
var slidesDiv = _createDiv({"class": "slides"});
|
||||||
|
var footerDiv = _createDiv({"class": "footer"});
|
||||||
|
slidesDiv.innerHTML = "<section><section><p></p></section></section>";
|
||||||
|
revealDiv.append(slidesDiv);
|
||||||
|
revealDiv.append(footerDiv);
|
||||||
|
document.body.style.transition = "opacity 75ms ease-in-out";
|
||||||
|
Display.init({isDisplay: true, doItemTransition: false});
|
||||||
|
});
|
||||||
|
afterEach(function() {
|
||||||
|
// Reset theme
|
||||||
|
Display._theme = null;
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should trigger dispatchEvent when toTransparent(eventName) is called with an event parameter", function (done) {
|
||||||
|
var testEventName = 'event_32';
|
||||||
|
displayWatcher.dispatchEvent = function(eventName) {
|
||||||
|
if (eventName == testEventName) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Display.toTransparent(testEventName);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
fail('dispatchEvent not called');
|
||||||
|
done();
|
||||||
|
}, TRANSITION_TIMEOUT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should trigger dispatchEvent when toBlack(eventName) is called with an event parameter", function (done) {
|
||||||
|
var testEventName = 'event_33';
|
||||||
|
displayWatcher.dispatchEvent = function(eventName) {
|
||||||
|
if (eventName == testEventName) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Display.toBlack(testEventName);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
fail('dispatchEvent not called');
|
||||||
|
done();
|
||||||
|
}, TRANSITION_TIMEOUT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should trigger dispatchEvent when toTheme(eventName) is called with an event parameter", function (done) {
|
||||||
|
var testEventName = 'event_34';
|
||||||
|
displayWatcher.dispatchEvent = function(eventName) {
|
||||||
|
if (eventName == testEventName) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Display.toTheme(testEventName);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
fail('dispatchEvent not called');
|
||||||
|
done();
|
||||||
|
}, TRANSITION_TIMEOUT);
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should trigger dispatchEvent when show(eventName) is called with an event parameter", function (done) {
|
||||||
|
var testEventName = 'event_35';
|
||||||
|
displayWatcher.dispatchEvent = function(eventName) {
|
||||||
|
if (eventName == testEventName) {
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
Display.show(testEventName);
|
||||||
|
|
||||||
|
setTimeout(function() {
|
||||||
|
fail('dispatchEvent not called');
|
||||||
|
done();
|
||||||
|
}, TRANSITION_TIMEOUT);
|
||||||
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe("Display.alert", function () {
|
describe("Display.alert", function () {
|
||||||
|
@ -33,7 +33,7 @@ from PyQt5 import QtCore
|
|||||||
# Mock QtWebEngineWidgets
|
# Mock QtWebEngineWidgets
|
||||||
sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
|
||||||
|
|
||||||
from openlp.core.display.window import DisplayWindow, DisplayWatcher
|
from openlp.core.display.window import TRANSITION_END_EVENT_NAME, DisplayWindow, DisplayWatcher
|
||||||
from openlp.core.common import is_win
|
from openlp.core.common import is_win
|
||||||
from openlp.core.common.enum import ServiceItemType
|
from openlp.core.common.enum import ServiceItemType
|
||||||
from openlp.core.lib.theme import Theme
|
from openlp.core.lib.theme import Theme
|
||||||
@ -602,20 +602,150 @@ def test_hide_display_to_transparent(display_window_env, mock_settings):
|
|||||||
assert display_window.setVisible.call_count == 0
|
assert display_window.setVisible.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_watcher_dispatches_registered_event(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that the display watcher dispatches events to the registered listeners
|
||||||
|
"""
|
||||||
|
# GIVEN: Display window and a dummy event
|
||||||
|
event_name = 'dummy_event'
|
||||||
|
event_listener = MagicMock()
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window.display_watcher.register_event_listener(event_name, event_listener)
|
||||||
|
|
||||||
|
# WHEN: Event is dispatched
|
||||||
|
display_window.display_watcher.dispatchEvent(event_name, {})
|
||||||
|
|
||||||
|
# THEN: Events should be called
|
||||||
|
event_listener.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_watcher_dispatches_permanent_registered_event(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that the display watcher dispatches events to the permanent registered listeners
|
||||||
|
"""
|
||||||
|
# GIVEN: Display window and a dummy event
|
||||||
|
event_name = 'dummy_event'
|
||||||
|
event_listener = MagicMock()
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window.display_watcher.register_event_listener(event_name, event_listener, True)
|
||||||
|
|
||||||
|
# WHEN: Event is dispatched
|
||||||
|
display_window.display_watcher.dispatchEvent(event_name, {})
|
||||||
|
|
||||||
|
# THEN: Events should be called
|
||||||
|
event_listener.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_watcher_dispatches_transient_and_permanent_registered_event(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that the display watcher dispatches events to both transient and permanent registered listeners
|
||||||
|
"""
|
||||||
|
# GIVEN: Display window and a dummy event
|
||||||
|
event_name = 'dummy_event'
|
||||||
|
event_listener = MagicMock()
|
||||||
|
event_listener_permanent = MagicMock()
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window.display_watcher.register_event_listener(event_name, event_listener, True)
|
||||||
|
display_window.display_watcher.register_event_listener(event_name, event_listener_permanent, False)
|
||||||
|
|
||||||
|
# WHEN: Event is dispatched
|
||||||
|
display_window.display_watcher.dispatchEvent(event_name, {})
|
||||||
|
|
||||||
|
# THEN: Events should be called
|
||||||
|
event_listener.assert_called_once()
|
||||||
|
event_listener_permanent.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_watcher_unregisters_registered_event(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that the display watcher unregisters registered listeners
|
||||||
|
"""
|
||||||
|
# GIVEN: Display window and a dummy event that is unregistered later
|
||||||
|
event_name = 'dummy_event'
|
||||||
|
event_listener = MagicMock()
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window.display_watcher.register_event_listener(event_name, event_listener)
|
||||||
|
display_window.display_watcher.unregister_event_listener(event_name)
|
||||||
|
|
||||||
|
# WHEN: Event is dispatched
|
||||||
|
display_window.display_watcher.dispatchEvent(event_name, {})
|
||||||
|
|
||||||
|
# THEN: Events should not be called
|
||||||
|
event_listener.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_watcher_unregisters_registered_permanent_event(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that the display watcher unregisters registered permanent listeners
|
||||||
|
"""
|
||||||
|
# GIVEN: Display window and a dummy event that is unregistered later
|
||||||
|
event_name = 'dummy_event'
|
||||||
|
event_listener = MagicMock()
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window.display_watcher.register_event_listener(event_name, event_listener, True)
|
||||||
|
display_window.display_watcher.unregister_event_listener(event_name)
|
||||||
|
|
||||||
|
# WHEN: Event is dispatched
|
||||||
|
display_window.display_watcher.dispatchEvent(event_name, {})
|
||||||
|
|
||||||
|
# THEN: Events should not be called
|
||||||
|
event_listener.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_watcher_unregisters_registered_permanent_and_transient_event(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that the display watcher unregisters registered listeners, both permanent and transient
|
||||||
|
"""
|
||||||
|
# GIVEN: Display window and a dummy event that is unregistered later
|
||||||
|
event_name = 'dummy_event'
|
||||||
|
event_listener = MagicMock()
|
||||||
|
event_listener_permanent = MagicMock()
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window.display_watcher.register_event_listener(event_name, event_listener)
|
||||||
|
display_window.display_watcher.register_event_listener(event_name, event_listener_permanent, False)
|
||||||
|
display_window.display_watcher.unregister_event_listener(event_name)
|
||||||
|
display_window.display_watcher.unregister_event_listener(event_name, False)
|
||||||
|
|
||||||
|
# WHEN: Event is dispatched
|
||||||
|
display_window.display_watcher.dispatchEvent(event_name, {})
|
||||||
|
|
||||||
|
# THEN: Events should not be called
|
||||||
|
event_listener.assert_not_called()
|
||||||
|
event_listener_permanent.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_hide_transparent_to_screen(display_window_env, mock_settings):
|
def test_hide_transparent_to_screen(display_window_env, mock_settings):
|
||||||
"""
|
"""
|
||||||
Test that when going transparent, and the disable transparent setting is enabled,
|
Test that when going transparent, and the disable transparent setting is enabled,
|
||||||
the screen mode should be used.
|
the screen mode should be used.
|
||||||
"""
|
"""
|
||||||
# GIVEN: Display window and setting advanced/disable transparent display = True
|
# GIVEN: Display window, setting advanced/disable transparent display = True and mocked run_javascript
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.setVisible = MagicMock()
|
display_window.setVisible = MagicMock()
|
||||||
|
has_ran_event = False
|
||||||
|
|
||||||
|
def set_has_ran_event(_):
|
||||||
|
nonlocal has_ran_event
|
||||||
|
has_ran_event = True
|
||||||
|
|
||||||
|
def on_dispatch_event(_):
|
||||||
|
display_window.display_watcher.register_event_listener(TRANSITION_END_EVENT_NAME, set_has_ran_event, False)
|
||||||
|
display_window.display_watcher.dispatchEvent(TRANSITION_END_EVENT_NAME, {})
|
||||||
|
|
||||||
|
display_window.run_javascript = MagicMock(side_effect=on_dispatch_event)
|
||||||
mock_settings.value.return_value = True
|
mock_settings.value.return_value = True
|
||||||
|
|
||||||
# WHEN: Hide display is run with HideMode.Screen
|
# WHEN: Hide display is run with HideMode.Screen
|
||||||
display_window.hide_display(HideMode.Screen)
|
display_window.hide_display(HideMode.Screen)
|
||||||
|
|
||||||
# THEN: Should run setVisible(False)
|
# THEN: Should run setVisible(False)
|
||||||
|
elapsed_time = 0
|
||||||
|
while not has_ran_event:
|
||||||
|
time.sleep(0.05)
|
||||||
|
elapsed_time += 0.05
|
||||||
|
if elapsed_time > 1:
|
||||||
|
break
|
||||||
|
assert has_ran_event is True
|
||||||
display_window.setVisible.assert_called_once_with(False)
|
display_window.setVisible.assert_called_once_with(False)
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user