mirror of https://gitlab.com/openlp/openlp.git
Display API abstraction
This commit is contained in:
parent
49190fe6b3
commit
a04ed99dfc
|
@ -12,6 +12,7 @@ module.exports = function(config) {
|
||||||
"tests/js/polyfill.js",
|
"tests/js/polyfill.js",
|
||||||
"tests/js/fake_webchannel.js",
|
"tests/js/fake_webchannel.js",
|
||||||
"openlp/core/display/html/reveal.js",
|
"openlp/core/display/html/reveal.js",
|
||||||
|
"openlp/core/display/html/display-init.js",
|
||||||
"openlp/core/display/html/display.js",
|
"openlp/core/display/html/display.js",
|
||||||
"tests/js/test_*.js"
|
"tests/js/test_*.js"
|
||||||
],
|
],
|
||||||
|
|
|
@ -46,8 +46,8 @@ from openlp.core.common.platform import is_macosx, is_win
|
||||||
from openlp.core.common.registry import Registry
|
from openlp.core.common.registry import Registry
|
||||||
from openlp.core.common.settings import Settings
|
from openlp.core.common.settings import Settings
|
||||||
from openlp.core.display.screens import ScreenList
|
from openlp.core.display.screens import ScreenList
|
||||||
|
from openlp.core.display.webengine import init_webview_custom_schemes, set_webview_display_path
|
||||||
from openlp.core.lib.filelock import FileLock
|
from openlp.core.lib.filelock import FileLock
|
||||||
from openlp.core.display.webengine import init_webview_custom_schemes
|
|
||||||
from openlp.core.loader import loader
|
from openlp.core.loader import loader
|
||||||
from openlp.core.resources import qInitResources
|
from openlp.core.resources import qInitResources
|
||||||
from openlp.core.server import Server
|
from openlp.core.server import Server
|
||||||
|
@ -295,6 +295,8 @@ def parse_options():
|
||||||
dir_name=os.path.join('<AppDir>', '..', '..')))
|
dir_name=os.path.join('<AppDir>', '..', '..')))
|
||||||
parser.add_argument('-w', '--no-web-server', dest='no_web_server', action='store_true',
|
parser.add_argument('-w', '--no-web-server', dest='no_web_server', action='store_true',
|
||||||
help='Turn off the Web and Socket Server ')
|
help='Turn off the Web and Socket Server ')
|
||||||
|
parser.add_argument('--display-custom-path', dest='display_custom_path', default=None,
|
||||||
|
help='Specify the custom path for display renderer (HTML). Useful for development.')
|
||||||
parser.add_argument('rargs', nargs='*', default=[])
|
parser.add_argument('rargs', nargs='*', default=[])
|
||||||
# Parse command line options and deal with them.
|
# Parse command line options and deal with them.
|
||||||
return parser.parse_args()
|
return parser.parse_args()
|
||||||
|
@ -485,6 +487,11 @@ def main():
|
||||||
Registry().register('settings_thread', settings_thread)
|
Registry().register('settings_thread', settings_thread)
|
||||||
Registry().register('application-qt', application)
|
Registry().register('application-qt', application)
|
||||||
Registry().register('application', app)
|
Registry().register('application', app)
|
||||||
|
if args.display_custom_path:
|
||||||
|
if (args.display_custom_path.startswith('http:') or args.display_custom_path.startswith('https:')):
|
||||||
|
Registry().register('display_custom_url', args.display_custom_path)
|
||||||
|
else:
|
||||||
|
set_webview_display_path(args.display_custom_path)
|
||||||
Registry().set_flag('no_web_server', args.no_web_server)
|
Registry().set_flag('no_web_server', args.no_web_server)
|
||||||
# Upgrade settings.
|
# Upgrade settings.
|
||||||
app.settings = settings
|
app.settings = settings
|
||||||
|
|
|
@ -114,12 +114,20 @@ class Registry(metaclass=Singleton):
|
||||||
|
|
||||||
def has_function(self, event):
|
def has_function(self, event):
|
||||||
"""
|
"""
|
||||||
Returns whether there's any hander associated with the event.
|
Returns whether there's any handler associated with the event.
|
||||||
|
|
||||||
:param event: The function to be checked
|
:param event: The function to be checked
|
||||||
"""
|
"""
|
||||||
return event in self.functions_list
|
return event in self.functions_list
|
||||||
|
|
||||||
|
def has(self, service_name: str) -> bool:
|
||||||
|
"""
|
||||||
|
Returns whether there's any service registered with provided name
|
||||||
|
|
||||||
|
:param service_name: The service name to be checked
|
||||||
|
"""
|
||||||
|
return service_name in self.service_list
|
||||||
|
|
||||||
def execute(self, event, *args, **kwargs):
|
def execute(self, event, *args, **kwargs):
|
||||||
"""
|
"""
|
||||||
Execute all the handlers associated with the event and return an array of results.
|
Execute all the handlers associated with the event and return an array of results.
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
/*jshint esversion: 9 */
|
||||||
|
class CommunicationBridge {
|
||||||
|
constructor() {
|
||||||
|
this.target = null;
|
||||||
|
this.initOptions = null;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestAction(action, ...values) {
|
||||||
|
if (action == 'init') {
|
||||||
|
this.initOptions = (values && values[0]) || null;
|
||||||
|
}
|
||||||
|
|
||||||
|
let returnValue;
|
||||||
|
if (this.target) {
|
||||||
|
returnValue = this.target._handleNativeCall(action, ...values);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action == 'init') {
|
||||||
|
this._onInitialized();
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
requestActionAsync(action, returnEvent, ...values) {
|
||||||
|
let returnValue;
|
||||||
|
if (this.target) {
|
||||||
|
returnValue = this.target._handleNativeCall(action, ...values);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (returnValue && ('then' in returnValue)) {
|
||||||
|
returnValue.then((value) => {
|
||||||
|
this._dispatchEvent(returnEvent, value || {});
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
this._dispatchEvent(returnEvent, returnValue || {});
|
||||||
|
}
|
||||||
|
|
||||||
|
return returnValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
setDisplayTarget(newTarget) {
|
||||||
|
this.target = newTarget;
|
||||||
|
if (this.initOptions) {
|
||||||
|
this.target._handleNativeCall('init', ...[this.initOptions]);
|
||||||
|
this._onInitialized();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
isReady() {
|
||||||
|
return !!this.target;
|
||||||
|
}
|
||||||
|
|
||||||
|
pleaseRepaint() {
|
||||||
|
if (window.displayWatcher) {
|
||||||
|
return window.displayWatcher.pleaseRepaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_onInitialized() {
|
||||||
|
if (window.displayWatcher) {
|
||||||
|
window.displayWatcher.setInitialised(true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_dispatchEvent(eventName, eventParameter) {
|
||||||
|
if (window.displayWatcher) {
|
||||||
|
window.displayWatcher.dispatchEvent(eventName, eventParameter || {});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function initNativeHandlerIfAvailable() {
|
||||||
|
if (window.QWebChannel) {
|
||||||
|
// Means we're running inside OpenLP
|
||||||
|
new window.QWebChannel(window.qt.webChannelTransport, (channel) => {
|
||||||
|
window.displayWatcher = channel.objects.displayWatcher;
|
||||||
|
|
||||||
|
// Defining window title as exposed by OpenLP
|
||||||
|
(window.displayWatcher.getWindowTitle || (() => Promise.resolve('')))()
|
||||||
|
.then((windowTitle) => {
|
||||||
|
if (windowTitle) {
|
||||||
|
const titleTag = document.head.getElementsByTagName('title')[0];
|
||||||
|
if (titleTag) {
|
||||||
|
titleTag.innerText = `${titleTag.innerText} (${windowTitle})`;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
window.initCommunicationBridge = () => {
|
||||||
|
initNativeHandlerIfAvailable();
|
||||||
|
var communicationBridge = new CommunicationBridge();
|
||||||
|
window.communicationBridge = communicationBridge;
|
||||||
|
window.requestAction = communicationBridge.requestAction.bind(communicationBridge);
|
||||||
|
window.requestActionAsync = communicationBridge.requestActionAsync.bind(communicationBridge);
|
||||||
|
window.isReady = communicationBridge.isReady.bind(communicationBridge);
|
||||||
|
};
|
|
@ -6,6 +6,7 @@
|
||||||
<link type="text/css" rel="stylesheet" href="display.css">
|
<link type="text/css" rel="stylesheet" href="display.css">
|
||||||
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
<script type="text/javascript" src="qrc:///qtwebchannel/qwebchannel.js"></script>
|
||||||
<script type="text/javascript" src="reveal.js"></script>
|
<script type="text/javascript" src="reveal.js"></script>
|
||||||
|
<script type="text/javascript" src="display-init.js"></script>
|
||||||
<script type="text/javascript" src="display.js"></script>
|
<script type="text/javascript" src="display.js"></script>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
* display.js is the main Javascript file that is used to drive the display.
|
* display.js is the main Javascript file that is used to drive the display.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Background type enumeration
|
* Background type enumeration
|
||||||
*/
|
*/
|
||||||
|
@ -892,7 +893,8 @@ var Display = {
|
||||||
/**
|
/**
|
||||||
* Blank the screen
|
* Blank the screen
|
||||||
*/
|
*/
|
||||||
toBlack: function (onFinishedEventName) {
|
toBlack: function () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
/* Avoid race conditions where display goes to transparent and quickly goes to black */
|
/* Avoid race conditions where display goes to transparent and quickly goes to black */
|
||||||
Display._abortLastTransitionOperation();
|
Display._abortLastTransitionOperation();
|
||||||
/*
|
/*
|
||||||
|
@ -907,16 +909,16 @@ var Display = {
|
||||||
Display._reenableGlobalTransitions(function() {
|
Display._reenableGlobalTransitions(function() {
|
||||||
var documentBody = $("body")[0];
|
var documentBody = $("body")[0];
|
||||||
documentBody.style.opacity = 1;
|
documentBody.style.opacity = 1;
|
||||||
if (onFinishedEventName) {
|
resolve();
|
||||||
displayWatcher.dispatchEvent(onFinishedEventName, {});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Hide all but theme background
|
* Hide all but theme background
|
||||||
*/
|
*/
|
||||||
toTheme: function (onFinishedEventName) {
|
toTheme: function () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
Display._abortLastTransitionOperation();
|
Display._abortLastTransitionOperation();
|
||||||
/*
|
/*
|
||||||
Reveal's black overlay should be shown before the transitions are
|
Reveal's black overlay should be shown before the transitions are
|
||||||
|
@ -931,15 +933,15 @@ var Display = {
|
||||||
Reveal.togglePause();
|
Reveal.togglePause();
|
||||||
}
|
}
|
||||||
Display._reenableGlobalTransitions(function() {
|
Display._reenableGlobalTransitions(function() {
|
||||||
if (onFinishedEventName) {
|
resolve();
|
||||||
displayWatcher.dispatchEvent(onFinishedEventName, {});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Hide everything (CAUTION: Causes a invisible mouse barrier)
|
* Hide everything (CAUTION: Causes a invisible mouse barrier)
|
||||||
*/
|
*/
|
||||||
toTransparent: function (onFinishedEventName) {
|
toTransparent: function () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
Display._abortLastTransitionOperation();
|
Display._abortLastTransitionOperation();
|
||||||
var documentBody = $("body")[0];
|
var documentBody = $("body")[0];
|
||||||
documentBody.style.opacity = 0;
|
documentBody.style.opacity = 0;
|
||||||
|
@ -979,16 +981,16 @@ var Display = {
|
||||||
Display._requestAnimationFrameExclusive(function() {
|
Display._requestAnimationFrameExclusive(function() {
|
||||||
/* We're transparent now, aborting any transition event between */
|
/* We're transparent now, aborting any transition event between */
|
||||||
Display._abortLastTransitionOperation();
|
Display._abortLastTransitionOperation();
|
||||||
if (onFinishedEventName) {
|
resolve();
|
||||||
displayWatcher.dispatchEvent(onFinishedEventName, {});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* Show the screen
|
* Show the screen
|
||||||
*/
|
*/
|
||||||
show: function (onFinishedEventName) {
|
show: function () {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
var documentBody = $("body")[0];
|
var documentBody = $("body")[0];
|
||||||
/*
|
/*
|
||||||
Removing transitionend event, avoids the content being hidden if the user
|
Removing transitionend event, avoids the content being hidden if the user
|
||||||
|
@ -1005,9 +1007,8 @@ var Display = {
|
||||||
Display._restorePauseBehavior();
|
Display._restorePauseBehavior();
|
||||||
Display._reenableGlobalTransitions(function() {
|
Display._reenableGlobalTransitions(function() {
|
||||||
documentBody.style.opacity = 1;
|
documentBody.style.opacity = 1;
|
||||||
if (onFinishedEventName) {
|
resolve();
|
||||||
displayWatcher.dispatchEvent(onFinishedEventName, {});
|
});
|
||||||
}
|
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -1413,6 +1414,12 @@ var Display = {
|
||||||
return url;
|
return url;
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
new QWebChannel(qt.webChannelTransport, function (channel) {
|
|
||||||
window.displayWatcher = channel.objects.displayWatcher;
|
Display._handleNativeCall = (action, ...values) => {
|
||||||
});
|
if (Display[action]) {
|
||||||
|
return Display[action](...values);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
initCommunicationBridge();
|
||||||
|
communicationBridge.setDisplayTarget(Display);
|
||||||
|
|
|
@ -557,13 +557,13 @@ class ThemePreviewRenderer(DisplayWindow, LogMixin):
|
||||||
"""
|
"""
|
||||||
Calculate the number of lines that fits on one slide
|
Calculate the number of lines that fits on one slide
|
||||||
"""
|
"""
|
||||||
return self.run_javascript('Display.calculateLineCount();', is_sync=True)
|
return self.run_in_display('calculateLineCount', is_sync=True)
|
||||||
|
|
||||||
def clear_slides(self):
|
def clear_slides(self):
|
||||||
"""
|
"""
|
||||||
Clear slides
|
Clear slides
|
||||||
"""
|
"""
|
||||||
return self.run_javascript('Display.clearSlides();')
|
return self.run_in_display('clearSlides')
|
||||||
|
|
||||||
def generate_footer(self):
|
def generate_footer(self):
|
||||||
"""
|
"""
|
||||||
|
@ -859,10 +859,9 @@ class ThemePreviewRenderer(DisplayWindow, LogMixin):
|
||||||
return True
|
return True
|
||||||
self.clear_slides()
|
self.clear_slides()
|
||||||
self.log_debug('_text_fits_on_slide: 1\n{text}'.format(text=text))
|
self.log_debug('_text_fits_on_slide: 1\n{text}'.format(text=text))
|
||||||
self.run_javascript('Display.setTextSlide("{text}");'
|
self.run_in_display('setTextSlide', text.replace('"', '\\"'), is_sync=True)
|
||||||
.format(text=text.replace('"', '\\"')), is_sync=True)
|
|
||||||
self.log_debug('_text_fits_on_slide: 2')
|
self.log_debug('_text_fits_on_slide: 2')
|
||||||
does_text_fit = self.run_javascript('Display.doesContentFit();', is_sync=True)
|
does_text_fit = self.run_in_display('doesContentFit', is_sync=True)
|
||||||
return does_text_fit
|
return does_text_fit
|
||||||
|
|
||||||
def save_screenshot(self, fname=None):
|
def save_screenshot(self, fname=None):
|
||||||
|
|
|
@ -212,7 +212,7 @@ class WebViewCustomScheme(QtCore.QObject):
|
||||||
| QtWebEngineCore.QWebEngineUrlScheme.Flag.LocalScheme
|
| QtWebEngineCore.QWebEngineUrlScheme.Flag.LocalScheme
|
||||||
| QtWebEngineCore.QWebEngineUrlScheme.Flag.LocalAccessAllowed)
|
| QtWebEngineCore.QWebEngineUrlScheme.Flag.LocalAccessAllowed)
|
||||||
|
|
||||||
def create_scheme_handler(self):
|
def create_scheme_handler(self) -> QtWebEngineCore.QWebEngineUrlSchemeHandler:
|
||||||
raise Exception('Needs to be implemented.')
|
raise Exception('Needs to be implemented.')
|
||||||
|
|
||||||
def init_handler(self, profile=None):
|
def init_handler(self, profile=None):
|
||||||
|
|
|
@ -51,12 +51,17 @@ class DisplayWatcher(QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
initialised = QtCore.pyqtSignal(bool)
|
initialised = QtCore.pyqtSignal(bool)
|
||||||
|
|
||||||
def __init__(self, parent):
|
def __init__(self, parent, window_title=None):
|
||||||
super().__init__()
|
super().__init__()
|
||||||
self._display_window = parent
|
self._display_window = parent
|
||||||
self._transient_dispatch_events = {}
|
self._transient_dispatch_events = {}
|
||||||
self._permanent_dispatch_events = {}
|
self._permanent_dispatch_events = {}
|
||||||
self._event_counter = 0
|
self._event_counter = 0
|
||||||
|
self._window_title = window_title
|
||||||
|
|
||||||
|
@QtCore.pyqtSlot(result=str)
|
||||||
|
def getWindowTitle(self):
|
||||||
|
return self._window_title
|
||||||
|
|
||||||
@QtCore.pyqtSlot(bool)
|
@QtCore.pyqtSlot(bool)
|
||||||
def setInitialised(self, is_initialised):
|
def setInitialised(self, is_initialised):
|
||||||
|
@ -130,7 +135,7 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
This is a window to show the output
|
This is a window to show the output
|
||||||
"""
|
"""
|
||||||
def __init__(self, parent=None, screen=None, can_show_startup_screen=True, start_hidden=False,
|
def __init__(self, parent=None, screen=None, can_show_startup_screen=True, start_hidden=False,
|
||||||
after_loaded_callback=None):
|
after_loaded_callback=None, window_title=None):
|
||||||
"""
|
"""
|
||||||
Create the display window
|
Create the display window
|
||||||
"""
|
"""
|
||||||
|
@ -149,6 +154,7 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
self._is_manual_close = False
|
self._is_manual_close = False
|
||||||
self._can_show_startup_screen = can_show_startup_screen
|
self._can_show_startup_screen = can_show_startup_screen
|
||||||
self._fbo = None
|
self._fbo = None
|
||||||
|
self.window_title = window_title
|
||||||
self.setWindowTitle(translate('OpenLP.DisplayWindow', 'Display Window'))
|
self.setWindowTitle(translate('OpenLP.DisplayWindow', 'Display Window'))
|
||||||
self.setWindowFlags(flags)
|
self.setWindowFlags(flags)
|
||||||
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
|
||||||
|
@ -162,14 +168,23 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
self.webview.display_clicked = self.disable_display
|
self.webview.display_clicked = self.disable_display
|
||||||
self.layout.addWidget(self.webview)
|
self.layout.addWidget(self.webview)
|
||||||
self.webview.loadFinished.connect(self.after_loaded)
|
self.webview.loadFinished.connect(self.after_loaded)
|
||||||
self.display_path = 'openlp://display/display.html'
|
|
||||||
self.checkerboard_path = 'openlp://display/checkerboard.png'
|
|
||||||
self.openlp_splash_screen_path = 'openlp://display/openlp-splash-screen.png'
|
|
||||||
self.channel = QtWebChannel.QWebChannel(self)
|
self.channel = QtWebChannel.QWebChannel(self)
|
||||||
self.display_watcher = DisplayWatcher(self)
|
if not window_title and screen and screen.is_display:
|
||||||
|
window_title = 'Display Window'
|
||||||
|
self.display_watcher = DisplayWatcher(self, window_title)
|
||||||
self.channel.registerObject('displayWatcher', self.display_watcher)
|
self.channel.registerObject('displayWatcher', self.display_watcher)
|
||||||
self.webview.page().setWebChannel(self.channel)
|
self.webview.page().setWebChannel(self.channel)
|
||||||
self.display_watcher.initialised.connect(self.on_initialised)
|
self.display_watcher.initialised.connect(self.on_initialised)
|
||||||
|
self.openlp_splash_screen_path = 'openlp://display/openlp-splash-screen.png'
|
||||||
|
# Using custom display if provided
|
||||||
|
if Registry().has('display_custom_url') and Registry().get('display_custom_url') is not None:
|
||||||
|
display_custom_url = Registry().get('display_custom_url')
|
||||||
|
self.display_path = display_custom_url + '/display.html'
|
||||||
|
self.checkerboard_path = display_custom_url + '/checkerboard.png'
|
||||||
|
qUrl = QtCore.QUrl(display_custom_url)
|
||||||
|
else:
|
||||||
|
self.display_path = 'openlp://display/display.html'
|
||||||
|
self.checkerboard_path = 'openlp://display/checkerboard.png'
|
||||||
qUrl = QtCore.QUrl(self.display_path)
|
qUrl = QtCore.QUrl(self.display_path)
|
||||||
self.set_url(qUrl)
|
self.set_url(qUrl)
|
||||||
self.is_display = False
|
self.is_display = False
|
||||||
|
@ -248,7 +263,7 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
|
|
||||||
def set_background_image(self, image_path):
|
def set_background_image(self, image_path):
|
||||||
image_uri = image_path.as_uri()
|
image_uri = image_path.as_uri()
|
||||||
self.run_javascript('Display.setBackgroundImage("{image}");'.format(image=image_uri))
|
self.run_in_display('setBackgroundImage', image_uri)
|
||||||
|
|
||||||
def set_single_image(self, bg_color, image_path):
|
def set_single_image(self, bg_color, image_path):
|
||||||
"""
|
"""
|
||||||
|
@ -256,12 +271,10 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
:param Path image_path: Path to the image
|
:param Path image_path: Path to the image
|
||||||
"""
|
"""
|
||||||
image_uri = image_path.as_uri()
|
image_uri = image_path.as_uri()
|
||||||
self.run_javascript('Display.setFullscreenImage("{bg_color}", "{image}");'.format(bg_color=bg_color,
|
self.run_in_display('setFullscreenImage', bg_color, image_uri)
|
||||||
image=image_uri))
|
|
||||||
|
|
||||||
def set_single_image_data(self, bg_color, image_data):
|
def set_single_image_data(self, bg_color, image_data):
|
||||||
self.run_javascript('Display.setFullscreenImageFromData("{bg_color}", '
|
self.run_in_display('setFullscreenImageFromData', bg_color, image_data)
|
||||||
'"{image_data}");'.format(bg_color=bg_color, image_data=image_data))
|
|
||||||
|
|
||||||
def set_startup_screen(self):
|
def set_startup_screen(self):
|
||||||
bg_color = self.settings.value('core/logo background color')
|
bg_color = self.settings.value('core/logo background color')
|
||||||
|
@ -276,8 +289,7 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
# if set to hide logo on startup, do not send the logo
|
# if set to hide logo on startup, do not send the logo
|
||||||
if self.settings.value('core/logo hide on startup'):
|
if self.settings.value('core/logo hide on startup'):
|
||||||
image_uri = ''
|
image_uri = ''
|
||||||
self.run_javascript('Display.setStartupSplashScreen("{bg_color}", "{image}");'.format(bg_color=bg_color,
|
self.run_in_display('setStartupSplashScreen', bg_color, image_uri)
|
||||||
image=image_uri))
|
|
||||||
|
|
||||||
def set_url(self, url):
|
def set_url(self, url):
|
||||||
"""
|
"""
|
||||||
|
@ -299,18 +311,16 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
"""
|
"""
|
||||||
Add stuff after page initialisation
|
Add stuff after page initialisation
|
||||||
"""
|
"""
|
||||||
js_is_display = str(self.is_display).lower()
|
item_transitions = self.settings.value('themes/item transitions')
|
||||||
item_transitions = str(self.settings.value('themes/item transitions')).lower()
|
hide_mouse = (self.settings.value('advanced/hide mouse') and self.is_display)
|
||||||
hide_mouse = str(self.settings.value('advanced/hide mouse') and self.is_display).lower()
|
slide_numbers_in_footer = self.settings.value('advanced/slide numbers in footer')
|
||||||
slide_numbers_in_footer = str(self.settings.value('advanced/slide numbers in footer')).lower()
|
self.run_in_display('init', {
|
||||||
self.run_javascript('Display.init({{'
|
'isDisplay': self.is_display,
|
||||||
'isDisplay: {is_display},'
|
'doItemTransitions': item_transitions,
|
||||||
'doItemTransitions: {do_item_transitions},'
|
'slideNumbersInFooter': slide_numbers_in_footer,
|
||||||
'slideNumbersInFooter: {slide_numbers_in_footer},'
|
'hideMouse': hide_mouse,
|
||||||
'hideMouse: {hide_mouse}'
|
'displayTitle': self.window_title
|
||||||
'}});'
|
})
|
||||||
.format(is_display=js_is_display, do_item_transitions=item_transitions,
|
|
||||||
slide_numbers_in_footer=slide_numbers_in_footer, hide_mouse=hide_mouse))
|
|
||||||
wait_for(lambda: self._is_initialised)
|
wait_for(lambda: self._is_initialised)
|
||||||
if self.scale != 1:
|
if self.scale != 1:
|
||||||
self.set_scale(self.scale)
|
self.set_scale(self.scale)
|
||||||
|
@ -319,7 +329,33 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
if self.after_loaded_callback:
|
if self.after_loaded_callback:
|
||||||
self.after_loaded_callback()
|
self.after_loaded_callback()
|
||||||
|
|
||||||
def run_javascript(self, script, is_sync=False):
|
def run_in_display(self, action, *parameters, raw_parameters=None, is_sync=False, return_event_name=None):
|
||||||
|
if len(parameters):
|
||||||
|
raw_parameters = ''
|
||||||
|
first = True
|
||||||
|
for parameter in parameters:
|
||||||
|
if not first:
|
||||||
|
raw_parameters += ', '
|
||||||
|
else:
|
||||||
|
first = False
|
||||||
|
raw_parameters += json.dumps(parameter)
|
||||||
|
action_name = 'requestAction'
|
||||||
|
action_async = ''
|
||||||
|
if return_event_name:
|
||||||
|
action_name = 'requestActionAsync'
|
||||||
|
action_async = ', \'{event_name}\''.format(event_name=return_event_name)
|
||||||
|
if not raw_parameters:
|
||||||
|
return self._run_javascript('{action_name}(\'{action}\'{action_async})'.format(action_name=action_name,
|
||||||
|
action=action,
|
||||||
|
action_async=action_async),
|
||||||
|
is_sync)
|
||||||
|
else:
|
||||||
|
return self._run_javascript('{action_name}(\'{action}\'{action_async}, {raw_parameters})'
|
||||||
|
.format(action_name=action_name, action=action, action_async=action_async,
|
||||||
|
raw_parameters=raw_parameters),
|
||||||
|
is_sync)
|
||||||
|
|
||||||
|
def _run_javascript(self, script, is_sync=False):
|
||||||
"""
|
"""
|
||||||
Run some Javascript in the WebView
|
Run some Javascript in the WebView
|
||||||
|
|
||||||
|
@ -355,14 +391,13 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
|
|
||||||
:param str verse: The verse to go to, e.g. "V1" for songs, or just "0" for other types
|
:param str verse: The verse to go to, e.g. "V1" for songs, or just "0" for other types
|
||||||
"""
|
"""
|
||||||
self.run_javascript('Display.goToSlide("{verse}");'.format(verse=verse))
|
self.run_in_display('goToSlide', verse)
|
||||||
|
|
||||||
def load_verses(self, verses, is_sync=False):
|
def load_verses(self, verses, is_sync=False):
|
||||||
"""
|
"""
|
||||||
Set verses in the display
|
Set verses in the display
|
||||||
"""
|
"""
|
||||||
json_verses = json.dumps(verses)
|
self.run_in_display('setTextSlides', verses, is_sync=is_sync)
|
||||||
self.run_javascript('Display.setTextSlides({verses});'.format(verses=json_verses), is_sync=is_sync)
|
|
||||||
|
|
||||||
def load_images(self, images):
|
def load_images(self, images):
|
||||||
"""
|
"""
|
||||||
|
@ -377,65 +412,7 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
image['thumbnail'] = image['thumbnail'].as_uri()
|
image['thumbnail'] = image['thumbnail'].as_uri()
|
||||||
else:
|
else:
|
||||||
image['thumbnail'] = image['path']
|
image['thumbnail'] = image['path']
|
||||||
json_images = json.dumps(imagesr)
|
self.run_in_display('setImageSlides', imagesr)
|
||||||
self.run_javascript('Display.setImageSlides({images});'.format(images=json_images))
|
|
||||||
|
|
||||||
def load_video(self, video):
|
|
||||||
"""
|
|
||||||
Load video in the display
|
|
||||||
"""
|
|
||||||
video = copy.deepcopy(video)
|
|
||||||
video['path'] = video['path'].as_uri()
|
|
||||||
json_video = json.dumps(video)
|
|
||||||
self.run_javascript('Display.setVideo({video});'.format(video=json_video))
|
|
||||||
|
|
||||||
def play_video(self):
|
|
||||||
"""
|
|
||||||
Play the currently loaded video
|
|
||||||
"""
|
|
||||||
self.run_javascript('Display.playVideo();')
|
|
||||||
|
|
||||||
def pause_video(self):
|
|
||||||
"""
|
|
||||||
Pause the currently playing video
|
|
||||||
"""
|
|
||||||
self.run_javascript('Display.pauseVideo();')
|
|
||||||
|
|
||||||
def stop_video(self):
|
|
||||||
"""
|
|
||||||
Stop the currently playing video
|
|
||||||
"""
|
|
||||||
self.run_javascript('Display.stopVideo();')
|
|
||||||
|
|
||||||
def set_video_playback_rate(self, rate):
|
|
||||||
"""
|
|
||||||
Set the playback rate of the current video.
|
|
||||||
|
|
||||||
The rate can be any valid float, with 0.0 being stopped, 1.0 being normal speed,
|
|
||||||
over 1.0 is faster, under 1.0 is slower, and negative is backwards.
|
|
||||||
|
|
||||||
:param rate: A float indicating the playback rate.
|
|
||||||
"""
|
|
||||||
self.run_javascript('Display.setPlaybackRate({rate});'.format(rate=rate))
|
|
||||||
|
|
||||||
def set_video_volume(self, level):
|
|
||||||
"""
|
|
||||||
Set the volume of the current video.
|
|
||||||
|
|
||||||
The volume should be an int from 0 to 100, where 0 is no sound and 100 is maximum volume. Any
|
|
||||||
values outside this range will raise a ``ValueError``.
|
|
||||||
|
|
||||||
:param level: A number between 0 and 100
|
|
||||||
"""
|
|
||||||
if level < 0 or level > 100:
|
|
||||||
raise ValueError('Volume should be from 0 to 100, was "{}"'.format(level))
|
|
||||||
self.run_javascript('Display.setVideoVolume({level});'.format(level=level))
|
|
||||||
|
|
||||||
def toggle_video_mute(self):
|
|
||||||
"""
|
|
||||||
Toggle the mute of the current video
|
|
||||||
"""
|
|
||||||
self.run_javascript('Display.toggleVideoMute();')
|
|
||||||
|
|
||||||
def save_screenshot(self, fname=None):
|
def save_screenshot(self, fname=None):
|
||||||
"""
|
"""
|
||||||
|
@ -477,7 +454,7 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
theme_copy.font_main_name = self._fix_font_name(theme.font_main_name)
|
theme_copy.font_main_name = self._fix_font_name(theme.font_main_name)
|
||||||
theme_copy.font_footer_name = self._fix_font_name(theme.font_footer_name)
|
theme_copy.font_footer_name = self._fix_font_name(theme.font_footer_name)
|
||||||
exported_theme = theme_copy.export_theme(is_js=True)
|
exported_theme = theme_copy.export_theme(is_js=True)
|
||||||
self.run_javascript('Display.setTheme({theme});'.format(theme=exported_theme), is_sync=is_sync)
|
self.run_in_display('setTheme', raw_parameters=exported_theme, is_sync=is_sync)
|
||||||
|
|
||||||
def reload_theme(self):
|
def reload_theme(self):
|
||||||
"""
|
"""
|
||||||
|
@ -485,13 +462,13 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
DO NOT use this when changing slides. Only use this if you need to force an update
|
DO NOT use this when changing slides. Only use this if you need to force an update
|
||||||
to the current visible slides.
|
to the current visible slides.
|
||||||
"""
|
"""
|
||||||
self.run_javascript('Display.resetTheme();')
|
self.run_in_display('resetTheme')
|
||||||
|
|
||||||
def get_video_types(self):
|
def get_video_types(self):
|
||||||
"""
|
"""
|
||||||
Get the types of videos playable by the embedded media player
|
Get the types of videos playable by the embedded media player
|
||||||
"""
|
"""
|
||||||
return self.run_javascript('Display.getVideoTypes();', is_sync=True)
|
return self.run_in_display('getVideoTypes', is_sync=True)
|
||||||
|
|
||||||
def show_display(self):
|
def show_display(self):
|
||||||
"""
|
"""
|
||||||
|
@ -505,7 +482,7 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
self.display_watcher.unregister_event_listener(TRANSITION_END_EVENT_NAME)
|
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.run_in_display('show')
|
||||||
self.hide_mode = None
|
self.hide_mode = None
|
||||||
|
|
||||||
def hide_display(self, mode=HideMode.Screen):
|
def hide_display(self, mode=HideMode.Screen):
|
||||||
|
@ -530,13 +507,13 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
# Hide window only after all webview CSS ransitions are done
|
# Hide window only after all webview CSS ransitions are done
|
||||||
self.display_watcher.register_event_listener(TRANSITION_END_EVENT_NAME,
|
self.display_watcher.register_event_listener(TRANSITION_END_EVENT_NAME,
|
||||||
lambda _: self.setVisible(False))
|
lambda _: self.setVisible(False))
|
||||||
self.run_javascript("Display.toTransparent('{}');".format(TRANSITION_END_EVENT_NAME))
|
self.run_in_display('toTransparent', return_event_name=TRANSITION_END_EVENT_NAME)
|
||||||
else:
|
else:
|
||||||
self.run_javascript('Display.toTransparent();')
|
self.run_in_display('toTransparent')
|
||||||
elif mode == HideMode.Blank:
|
elif mode == HideMode.Blank:
|
||||||
self.run_javascript('Display.toBlack();')
|
self.run_in_display('toBlack')
|
||||||
elif mode == HideMode.Theme:
|
elif mode == HideMode.Theme:
|
||||||
self.run_javascript('Display.toTheme();')
|
self.run_in_display('toTheme')
|
||||||
self.hide_mode = mode
|
self.hide_mode = mode
|
||||||
|
|
||||||
def disable_display(self):
|
def disable_display(self):
|
||||||
|
@ -554,7 +531,7 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
This function ensures that the current item won't flash momentarily when the webengineview
|
This function ensures that the current item won't flash momentarily when the webengineview
|
||||||
is displayed for a subsequent song or image.
|
is displayed for a subsequent song or image.
|
||||||
"""
|
"""
|
||||||
self.run_javascript('Display.finishWithCurrentItem();', True)
|
self.run_in_display('finishWithCurrentItem', is_sync=True)
|
||||||
self.webview.update()
|
self.webview.update()
|
||||||
|
|
||||||
def set_scale(self, scale):
|
def set_scale(self, scale):
|
||||||
|
@ -564,7 +541,7 @@ class DisplayWindow(QtWidgets.QWidget, RegistryProperties, LogMixin):
|
||||||
self.scale = scale
|
self.scale = scale
|
||||||
# Only scale if initialised (scale run again once initialised)
|
# Only scale if initialised (scale run again once initialised)
|
||||||
if self._is_initialised:
|
if self._is_initialised:
|
||||||
self.run_javascript('Display.setScale({scale});'.format(scale=scale * 100))
|
self.run_in_display('setScale', scale * 100)
|
||||||
|
|
||||||
def alert(self, text, settings):
|
def alert(self, text, settings):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -39,7 +39,7 @@ def loader():
|
||||||
MediaController()
|
MediaController()
|
||||||
PluginManager()
|
PluginManager()
|
||||||
# Set up the path with plugins
|
# Set up the path with plugins
|
||||||
Renderer()
|
Renderer(window_title='Renderer')
|
||||||
# Create slide controllers
|
# Create slide controllers
|
||||||
PreviewController()
|
PreviewController()
|
||||||
LiveController()
|
LiveController()
|
||||||
|
|
|
@ -147,7 +147,8 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
||||||
if screen.is_display:
|
if screen.is_display:
|
||||||
will_start_hidden = self._current_hide_mode == HideMode.Screen
|
will_start_hidden = self._current_hide_mode == HideMode.Screen
|
||||||
display = DisplayWindow(self, screen, start_hidden=will_start_hidden,
|
display = DisplayWindow(self, screen, start_hidden=will_start_hidden,
|
||||||
after_loaded_callback=self._display_after_loaded_callback)
|
after_loaded_callback=self._display_after_loaded_callback,
|
||||||
|
window_title='Live Screen' if self.is_live else 'Preview Screen')
|
||||||
self.displays.append(display)
|
self.displays.append(display)
|
||||||
self._reset_blank(False)
|
self._reset_blank(False)
|
||||||
if self.display:
|
if self.display:
|
||||||
|
@ -398,7 +399,7 @@ class SlideController(QtWidgets.QWidget, LogMixin, RegistryProperties):
|
||||||
self.slide_layout.setSpacing(0)
|
self.slide_layout.setSpacing(0)
|
||||||
self.slide_layout.setObjectName('SlideLayout')
|
self.slide_layout.setObjectName('SlideLayout')
|
||||||
# Set up the preview display
|
# Set up the preview display
|
||||||
self.preview_display = DisplayWindow(self)
|
self.preview_display = DisplayWindow(self, window_title='Live' if self.is_live else 'Preview')
|
||||||
self.slide_layout.addWidget(self.preview_display)
|
self.slide_layout.addWidget(self.preview_display)
|
||||||
self.slide_layout.resize.connect(self.on_preview_resize)
|
self.slide_layout.resize.connect(self.on_preview_resize)
|
||||||
# Actual preview screen
|
# Actual preview screen
|
||||||
|
|
|
@ -48,7 +48,8 @@ class UiThemeProgressDialog(object):
|
||||||
self.theme_preview_layout.margin = 8
|
self.theme_preview_layout.margin = 8
|
||||||
self.theme_preview_layout.setSpacing(0)
|
self.theme_preview_layout.setSpacing(0)
|
||||||
self.theme_preview_layout.setObjectName('preview_web_layout')
|
self.theme_preview_layout.setObjectName('preview_web_layout')
|
||||||
self.theme_display = ThemePreviewRenderer(theme_progress_dialog, can_show_startup_screen=False)
|
self.theme_display = ThemePreviewRenderer(theme_progress_dialog, can_show_startup_screen=False,
|
||||||
|
window_title='Theme Progress Dialog')
|
||||||
self.theme_display.setObjectName('theme_display')
|
self.theme_display.setObjectName('theme_display')
|
||||||
self.theme_preview_layout.addWidget(self.theme_display)
|
self.theme_preview_layout.addWidget(self.theme_display)
|
||||||
self.theme_progress_layout.addWidget(self.preview_area)
|
self.theme_progress_layout.addWidget(self.preview_area)
|
||||||
|
|
|
@ -100,7 +100,7 @@ class Ui_ThemeWizard(object):
|
||||||
self.preview_area_layout.margin = 8
|
self.preview_area_layout.margin = 8
|
||||||
self.preview_area_layout.setSpacing(0)
|
self.preview_area_layout.setSpacing(0)
|
||||||
self.preview_area_layout.setObjectName('preview_web_layout')
|
self.preview_area_layout.setObjectName('preview_web_layout')
|
||||||
self.preview_box = ThemePreviewRenderer(self)
|
self.preview_box = ThemePreviewRenderer(self, window_title="Theme Editor Preview")
|
||||||
self.preview_box.setObjectName('preview_box')
|
self.preview_box.setObjectName('preview_box')
|
||||||
self.preview_area_layout.addWidget(self.preview_box)
|
self.preview_area_layout.addWidget(self.preview_box)
|
||||||
self.preview_layout.addWidget(self.preview_area)
|
self.preview_layout.addWidget(self.preview_area)
|
||||||
|
|
|
@ -22,7 +22,6 @@
|
||||||
The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of the plugin which manages storing and
|
The :mod:`~openlp.plugins.alerts.lib.alertsmanager` module contains the part of the plugin which manages storing and
|
||||||
displaying of alerts.
|
displaying of alerts.
|
||||||
"""
|
"""
|
||||||
import json
|
|
||||||
|
|
||||||
from PyQt5 import QtCore, QtGui
|
from PyQt5 import QtCore, QtGui
|
||||||
|
|
||||||
|
@ -77,7 +76,7 @@ class AlertsManager(QtCore.QObject, RegistryBase, LogMixin, RegistryProperties):
|
||||||
'repeat': self.settings.value('alerts/repeat'),
|
'repeat': self.settings.value('alerts/repeat'),
|
||||||
'scroll': self.settings.value('alerts/scroll')
|
'scroll': self.settings.value('alerts/scroll')
|
||||||
}
|
}
|
||||||
self.live_controller.display.alert(text, json.dumps(alert_settings))
|
self.live_controller.display.alert(text, alert_settings)
|
||||||
|
|
||||||
def _hex_to_rgb(self, rgb_values):
|
def _hex_to_rgb(self, rgb_values):
|
||||||
"""
|
"""
|
||||||
|
|
|
@ -19,7 +19,7 @@
|
||||||
},
|
},
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"test": "karma start --single-run",
|
"test": "karma start --single-run",
|
||||||
"lint": "jshint openlp/core/display/html/display.js"
|
"lint": "jshint openlp/core/display/html/display*.js"
|
||||||
},
|
},
|
||||||
"jshintConfig": {
|
"jshintConfig": {
|
||||||
"esversion": 6
|
"esversion": 6
|
||||||
|
|
|
@ -1,9 +1,13 @@
|
||||||
function _createDiv(attrs) {
|
function _createDiv(attrs, inElement) {
|
||||||
var div = document.createElement("div");
|
var div = document.createElement("div");
|
||||||
for (key in attrs) {
|
for (key in attrs) {
|
||||||
div.setAttribute(key, attrs[key]);
|
div.setAttribute(key, attrs[key]);
|
||||||
}
|
}
|
||||||
|
if (inElement) {
|
||||||
|
inElement.appendChild(div);
|
||||||
|
} else {
|
||||||
document.body.appendChild(div);
|
document.body.appendChild(div);
|
||||||
|
}
|
||||||
return div;
|
return div;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -321,92 +325,6 @@ describe("Transitions", function () {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
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 () {
|
||||||
var alertContainer, alertBackground, alertText, settings, text;
|
var alertContainer, alertBackground, alertText, settings, text;
|
||||||
|
|
||||||
|
@ -785,8 +703,9 @@ describe("Display.setTextSlides", function () {
|
||||||
|
|
||||||
beforeEach(function() {
|
beforeEach(function() {
|
||||||
document.body.innerHTML = "";
|
document.body.innerHTML = "";
|
||||||
var slides_container = _createDiv({"class": "slides"});
|
var revealContainer = Display._revealContainer = _createDiv({"class": "reveal"})
|
||||||
var footer_container = _createDiv({"class": "footer"});
|
var slides_container = _createDiv({"class": "slides"}, revealContainer);
|
||||||
|
var footer_container = _createDiv({"class": "footer"}, revealContainer);
|
||||||
Display._slidesContainer = slides_container;
|
Display._slidesContainer = slides_container;
|
||||||
Display._footerContainer = footer_container;
|
Display._footerContainer = footer_container;
|
||||||
Display._slides = {};
|
Display._slides = {};
|
||||||
|
@ -1203,15 +1122,14 @@ describe("Reveal slidechanged event", function () {
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
|
|
||||||
var slidesDiv = _createDiv({"class": "slides"});
|
document.body.innerHTML = '';
|
||||||
|
var revealContainer = Display._revealContainer = _createDiv({"class": "reveal"});
|
||||||
|
|
||||||
|
var slidesDiv = _createDiv({"class": "slides"}, revealContainer);
|
||||||
slidesDiv.innerHTML = "<section><p></p></section>";
|
slidesDiv.innerHTML = "<section><p></p></section>";
|
||||||
Display._slidesContainer = slidesDiv;
|
Display._slidesContainer = slidesDiv;
|
||||||
var footerDiv = _createDiv({"class": "footer"});
|
var footerDiv = _createDiv({"class": "footer"}, revealContainer);
|
||||||
Display._footerContainer = footerDiv;
|
Display._footerContainer = footerDiv;
|
||||||
var revealDiv = _createDiv({"class": "reveal"});
|
|
||||||
revealDiv.append(slidesDiv);
|
|
||||||
revealDiv.append(footerDiv);
|
|
||||||
document.body.appendChild(revealDiv);
|
|
||||||
|
|
||||||
Display.init({isDisplay: false, doItemTransitions: false});
|
Display.init({isDisplay: false, doItemTransitions: false});
|
||||||
var oldDisplaySlideChanged = Display._onSlideChanged;
|
var oldDisplaySlideChanged = Display._onSlideChanged;
|
||||||
|
|
|
@ -0,0 +1,82 @@
|
||||||
|
"use strict";
|
||||||
|
|
||||||
|
describe("The CommunicationBridge object", () => {
|
||||||
|
/** @type {CommunicationBridge} */
|
||||||
|
let testCommunicationBridge;
|
||||||
|
let SimpleTarget;
|
||||||
|
|
||||||
|
beforeEach(() => {
|
||||||
|
testCommunicationBridge = new CommunicationBridge();
|
||||||
|
SimpleTarget = class {
|
||||||
|
_handleNativeCall(action, ...values) {}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it("should exist", () => {
|
||||||
|
expect(CommunicationBridge).toBeDefined()
|
||||||
|
});
|
||||||
|
it("should not be ready after instantiated", () => {
|
||||||
|
expect(testCommunicationBridge.isReady()).toBe(false);
|
||||||
|
});
|
||||||
|
it("should register target", () => {
|
||||||
|
testCommunicationBridge.setDisplayTarget(new SimpleTarget());
|
||||||
|
expect(testCommunicationBridge.isReady()).toBe(true);
|
||||||
|
|
||||||
|
});
|
||||||
|
it("should be able to requestAction", (done) => {
|
||||||
|
const target = new SimpleTarget()
|
||||||
|
target._handleNativeCall = (action, ...values) => done();
|
||||||
|
testCommunicationBridge.setDisplayTarget(target);
|
||||||
|
testCommunicationBridge.requestAction('dummy');
|
||||||
|
});
|
||||||
|
it("should be able to replay init action before setDisplayTarget", (done) => {
|
||||||
|
const target = new SimpleTarget()
|
||||||
|
target._handleNativeCall = (action, ...values) => {
|
||||||
|
if (action == 'init') {
|
||||||
|
expect(values[0]).toBe('replayed init');
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testCommunicationBridge.requestAction('init', 'replayed init');
|
||||||
|
testCommunicationBridge.setDisplayTarget(target);
|
||||||
|
});
|
||||||
|
it("should dispatch initialized event", (done) => {
|
||||||
|
displayWatcher.setInitialised = () => done();
|
||||||
|
testCommunicationBridge.setDisplayTarget(new SimpleTarget());
|
||||||
|
testCommunicationBridge.requestAction('init', {do_init: true});
|
||||||
|
});
|
||||||
|
it("should dispatch event on async call and promise-based handlers", (done) => {
|
||||||
|
const target = new SimpleTarget()
|
||||||
|
target._handleNativeCall = (action, ...values) => {
|
||||||
|
if (action == 'async') {
|
||||||
|
return new Promise((resolve) => resolve({value: 'async value'}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testCommunicationBridge.setDisplayTarget(target);
|
||||||
|
displayWatcher.dispatchEvent = (event, parameter) => {
|
||||||
|
expect(event).toBe('async_callback');
|
||||||
|
expect(parameter).toEqual({value: 'async value'});
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
// THEN: async callback should return value over displayWatch.dispatchEvent()
|
||||||
|
testCommunicationBridge.requestActionAsync('async', 'async_callback');
|
||||||
|
});
|
||||||
|
it("should dispatch event on async call and normal handlers", (done) => {
|
||||||
|
const target = new SimpleTarget()
|
||||||
|
target._handleNativeCall = (action, ...values) => {
|
||||||
|
if (action == 'sync') {
|
||||||
|
return {value: 'sync value'};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
testCommunicationBridge.setDisplayTarget(target);
|
||||||
|
displayWatcher.dispatchEvent = (event, parameter) => {
|
||||||
|
expect(event).toBe('sync_callback');
|
||||||
|
expect(parameter).toEqual({value: 'sync value'});
|
||||||
|
done();
|
||||||
|
}
|
||||||
|
|
||||||
|
// THEN: async callback should return value over displayWatch.dispatchEvent()
|
||||||
|
testCommunicationBridge.requestActionAsync('sync', 'sync_callback');
|
||||||
|
});
|
||||||
|
});
|
|
@ -173,13 +173,13 @@ def test_set_scale_not_initialised(display_window_env, mock_settings):
|
||||||
# GIVEN: A display window not yet initialised
|
# GIVEN: A display window not yet initialised
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window._is_initialised = False
|
display_window._is_initialised = False
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
|
|
||||||
# WHEN: set scale is run
|
# WHEN: set scale is run
|
||||||
display_window.set_scale(0.5)
|
display_window.set_scale(0.5)
|
||||||
|
|
||||||
# THEN: javascript should not be run
|
# THEN: javascript should not be run
|
||||||
display_window.run_javascript.assert_not_called()
|
display_window.run_in_display.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
def test_set_scale_initialised(display_window_env, mock_settings):
|
def test_set_scale_initialised(display_window_env, mock_settings):
|
||||||
|
@ -189,13 +189,31 @@ def test_set_scale_initialised(display_window_env, mock_settings):
|
||||||
# GIVEN: A initialised display window
|
# GIVEN: A initialised display window
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window._is_initialised = True
|
display_window._is_initialised = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
|
|
||||||
# WHEN: set scale is run
|
# WHEN: set scale is run
|
||||||
display_window.set_scale(0.5)
|
display_window.set_scale(0.5)
|
||||||
|
|
||||||
# THEN: javascript should not be run
|
# THEN: javascript should not be run
|
||||||
display_window.run_javascript.assert_called_once_with('Display.setScale(50.0);')
|
display_window.run_in_display.assert_called_once_with('setScale', 50.0)
|
||||||
|
|
||||||
|
|
||||||
|
def test_set_display_custom_url_works_http(registry, display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that setting a display custom url works with HTTP path
|
||||||
|
"""
|
||||||
|
# GIVEN: A mocked set_url and a custom display path
|
||||||
|
test_path = 'http://localhost:4200?testing=true'
|
||||||
|
registry.register('display_custom_url', test_path)
|
||||||
|
|
||||||
|
with patch('openlp.core.display.window.DisplayWindow.set_url') as mocked_set_url, \
|
||||||
|
patch('openlp.core.display.window.QtCore.QUrl') as mocked_qurl:
|
||||||
|
mocked_qurl.side_effect = lambda input: input
|
||||||
|
# WHEN: creating a DisplayWindow
|
||||||
|
DisplayWindow()
|
||||||
|
|
||||||
|
# THEN: URL should be set with the custom path
|
||||||
|
mocked_set_url.assert_called_once_with(test_path)
|
||||||
|
|
||||||
|
|
||||||
def test_set_startup_screen(display_window_env, mock_settings):
|
def test_set_startup_screen(display_window_env, mock_settings):
|
||||||
|
@ -205,7 +223,7 @@ def test_set_startup_screen(display_window_env, mock_settings):
|
||||||
# GIVEN: A display window and mocked settings with logo path
|
# GIVEN: A display window and mocked settings with logo path
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window._is_initialised = True
|
display_window._is_initialised = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
|
|
||||||
if is_win():
|
if is_win():
|
||||||
image_path = 'c:/my/image.png'
|
image_path = 'c:/my/image.png'
|
||||||
|
@ -225,8 +243,9 @@ def test_set_startup_screen(display_window_env, mock_settings):
|
||||||
display_window.set_startup_screen()
|
display_window.set_startup_screen()
|
||||||
|
|
||||||
# THEN: javascript should be run
|
# THEN: javascript should be run
|
||||||
display_window.run_javascript.assert_called_once_with(
|
display_window.run_in_display.assert_called_once_with('setStartupSplashScreen', "red",
|
||||||
'Display.setStartupSplashScreen("red", "openlp-library://local-file/{path}");'.format(path=expect_image_path))
|
"openlp-library://local-file/{path}"
|
||||||
|
.format(path=expect_image_path))
|
||||||
|
|
||||||
|
|
||||||
def test_set_startup_screen_default_image(display_window_env, mock_settings):
|
def test_set_startup_screen_default_image(display_window_env, mock_settings):
|
||||||
|
@ -236,7 +255,7 @@ def test_set_startup_screen_default_image(display_window_env, mock_settings):
|
||||||
# GIVEN: A display window and mocked settings with logo path
|
# GIVEN: A display window and mocked settings with logo path
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window._is_initialised = True
|
display_window._is_initialised = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
splash_screen_path = 'openlp://display/openlp-splash-screen.png'
|
splash_screen_path = 'openlp://display/openlp-splash-screen.png'
|
||||||
expect_splash_screen_path = splash_screen_path
|
expect_splash_screen_path = splash_screen_path
|
||||||
display_window.openlp_splash_screen_path = splash_screen_path
|
display_window.openlp_splash_screen_path = splash_screen_path
|
||||||
|
@ -251,8 +270,8 @@ def test_set_startup_screen_default_image(display_window_env, mock_settings):
|
||||||
display_window.set_startup_screen()
|
display_window.set_startup_screen()
|
||||||
|
|
||||||
# THEN: javascript should be run
|
# THEN: javascript should be run
|
||||||
display_window.run_javascript.assert_called_with(
|
display_window.run_in_display.assert_called_with('setStartupSplashScreen', 'blue',
|
||||||
'Display.setStartupSplashScreen("blue", "{path}");'.format(path=expect_splash_screen_path))
|
"{path}".format(path=expect_splash_screen_path))
|
||||||
|
|
||||||
|
|
||||||
def test_set_startup_screen_missing(display_window_env, mock_settings):
|
def test_set_startup_screen_missing(display_window_env, mock_settings):
|
||||||
|
@ -262,7 +281,7 @@ def test_set_startup_screen_missing(display_window_env, mock_settings):
|
||||||
# GIVEN: A display window and mocked settings with logo path missing
|
# GIVEN: A display window and mocked settings with logo path missing
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window._is_initialised = True
|
display_window._is_initialised = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
display_window.openlp_splash_screen_path = Path('/default/splash_screen.png')
|
display_window.openlp_splash_screen_path = Path('/default/splash_screen.png')
|
||||||
settings = {
|
settings = {
|
||||||
'core/logo background color': 'green',
|
'core/logo background color': 'green',
|
||||||
|
@ -275,8 +294,7 @@ def test_set_startup_screen_missing(display_window_env, mock_settings):
|
||||||
display_window.set_startup_screen()
|
display_window.set_startup_screen()
|
||||||
|
|
||||||
# THEN: javascript should be run
|
# THEN: javascript should be run
|
||||||
display_window.run_javascript.assert_called_with(
|
display_window.run_in_display.assert_called_with('setStartupSplashScreen', 'green', '')
|
||||||
'Display.setStartupSplashScreen("green", "");')
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_startup_screen_hide(display_window_env, mock_settings):
|
def test_set_startup_screen_hide(display_window_env, mock_settings):
|
||||||
|
@ -286,7 +304,7 @@ def test_set_startup_screen_hide(display_window_env, mock_settings):
|
||||||
# GIVEN: A display window and mocked settings with hide logo true
|
# GIVEN: A display window and mocked settings with hide logo true
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window._is_initialised = True
|
display_window._is_initialised = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
display_window.openlp_splash_screen_path = Path('/default/splash_screen.png')
|
display_window.openlp_splash_screen_path = Path('/default/splash_screen.png')
|
||||||
settings = {
|
settings = {
|
||||||
'core/logo background color': 'orange',
|
'core/logo background color': 'orange',
|
||||||
|
@ -299,8 +317,7 @@ def test_set_startup_screen_hide(display_window_env, mock_settings):
|
||||||
display_window.set_startup_screen()
|
display_window.set_startup_screen()
|
||||||
|
|
||||||
# THEN: javascript should be run
|
# THEN: javascript should be run
|
||||||
display_window.run_javascript.assert_called_once_with(
|
display_window.run_in_display.assert_called_once_with('setStartupSplashScreen', 'orange', '')
|
||||||
'Display.setStartupSplashScreen("orange", "");')
|
|
||||||
|
|
||||||
|
|
||||||
def test_after_loaded(display_window_env, mock_settings, registry):
|
def test_after_loaded(display_window_env, mock_settings, registry):
|
||||||
|
@ -313,7 +330,7 @@ def test_after_loaded(display_window_env, mock_settings, registry):
|
||||||
mock_settings.value.return_value = True
|
mock_settings.value.return_value = True
|
||||||
display_window.scale = 2
|
display_window.scale = 2
|
||||||
display_window._is_initialised = True
|
display_window._is_initialised = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
display_window.set_scale = MagicMock()
|
display_window.set_scale = MagicMock()
|
||||||
display_window.set_startup_screen = MagicMock()
|
display_window.set_startup_screen = MagicMock()
|
||||||
|
|
||||||
|
@ -321,17 +338,16 @@ def test_after_loaded(display_window_env, mock_settings, registry):
|
||||||
display_window.after_loaded()
|
display_window.after_loaded()
|
||||||
|
|
||||||
# THEN: The following functions should have been called
|
# THEN: The following functions should have been called
|
||||||
display_window.run_javascript.assert_called_once_with('Display.init({'
|
display_window.run_in_display.assert_called_once_with('init', {'isDisplay': True,
|
||||||
'isDisplay: true,'
|
'doItemTransitions': True,
|
||||||
'doItemTransitions: true,'
|
'slideNumbersInFooter': True,
|
||||||
'slideNumbersInFooter: true,'
|
'hideMouse': True,
|
||||||
'hideMouse: true'
|
'displayTitle': None})
|
||||||
'});')
|
|
||||||
display_window.set_scale.assert_called_once_with(2)
|
display_window.set_scale.assert_called_once_with(2)
|
||||||
display_window.set_startup_screen.assert_called_once()
|
display_window.set_startup_screen.assert_called_once()
|
||||||
|
|
||||||
|
|
||||||
def test_after_loaded_hide_mouse_not_display(display_window_env, mock_settings, registry):
|
def test_after_loaded_hide_mouse_not_display(display_window_env, mock_settings):
|
||||||
"""
|
"""
|
||||||
Test the mouse is showing even if the `hide mouse` setting is set while is_display=false
|
Test the mouse is showing even if the `hide mouse` setting is set while is_display=false
|
||||||
"""
|
"""
|
||||||
|
@ -341,7 +357,7 @@ def test_after_loaded_hide_mouse_not_display(display_window_env, mock_settings,
|
||||||
mock_settings.value.return_value = True
|
mock_settings.value.return_value = True
|
||||||
display_window.scale = 2
|
display_window.scale = 2
|
||||||
display_window._is_initialised = True
|
display_window._is_initialised = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
display_window.set_scale = MagicMock()
|
display_window.set_scale = MagicMock()
|
||||||
display_window.set_startup_screen = MagicMock()
|
display_window.set_startup_screen = MagicMock()
|
||||||
|
|
||||||
|
@ -349,12 +365,11 @@ def test_after_loaded_hide_mouse_not_display(display_window_env, mock_settings,
|
||||||
display_window.after_loaded()
|
display_window.after_loaded()
|
||||||
|
|
||||||
# THEN: Display.init should be called where is_display=false, do_item_transitions=true, show_mouse=false
|
# THEN: Display.init should be called where is_display=false, do_item_transitions=true, show_mouse=false
|
||||||
display_window.run_javascript.assert_called_once_with('Display.init({'
|
display_window.run_in_display.assert_called_once_with('init', {'isDisplay': False,
|
||||||
'isDisplay: false,'
|
'doItemTransitions': True,
|
||||||
'doItemTransitions: true,'
|
'slideNumbersInFooter': True,
|
||||||
'slideNumbersInFooter: true,'
|
'hideMouse': False,
|
||||||
'hideMouse: false'
|
'displayTitle': None})
|
||||||
'});')
|
|
||||||
|
|
||||||
|
|
||||||
def test_after_loaded_callback(display_window_env, mock_settings, registry):
|
def test_after_loaded_callback(display_window_env, mock_settings, registry):
|
||||||
|
@ -367,7 +382,7 @@ def test_after_loaded_callback(display_window_env, mock_settings, registry):
|
||||||
display_window.is_display = True
|
display_window.is_display = True
|
||||||
mock_settings.value.return_value = True
|
mock_settings.value.return_value = True
|
||||||
display_window._is_initialised = True
|
display_window._is_initialised = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
display_window.set_scale = MagicMock()
|
display_window.set_scale = MagicMock()
|
||||||
display_window.set_startup_screen = MagicMock()
|
display_window.set_startup_screen = MagicMock()
|
||||||
|
|
||||||
|
@ -379,7 +394,7 @@ def test_after_loaded_callback(display_window_env, mock_settings, registry):
|
||||||
|
|
||||||
|
|
||||||
@patch.object(time, 'time')
|
@patch.object(time, 'time')
|
||||||
def test_run_javascript_no_sync_no_wait(mock_time, display_window_env, mock_settings):
|
def test_run_in_display_no_sync_no_wait(mock_time, display_window_env, mock_settings):
|
||||||
"""
|
"""
|
||||||
test a script is run on the webview
|
test a script is run on the webview
|
||||||
"""
|
"""
|
||||||
|
@ -389,7 +404,7 @@ def test_run_javascript_no_sync_no_wait(mock_time, display_window_env, mock_sett
|
||||||
display_window.webview.page = MagicMock(return_value=webengine_page)
|
display_window.webview.page = MagicMock(return_value=webengine_page)
|
||||||
|
|
||||||
# WHEN: javascript is requested to run
|
# WHEN: javascript is requested to run
|
||||||
display_window.run_javascript('javascript to execute')
|
display_window._run_javascript('javascript to execute')
|
||||||
|
|
||||||
# THEN: javascript should be run with no delay
|
# THEN: javascript should be run with no delay
|
||||||
webengine_page.runJavaScript.assert_called_once_with('javascript to execute')
|
webengine_page.runJavaScript.assert_called_once_with('javascript to execute')
|
||||||
|
@ -397,7 +412,7 @@ def test_run_javascript_no_sync_no_wait(mock_time, display_window_env, mock_sett
|
||||||
|
|
||||||
|
|
||||||
@patch.object(time, 'time')
|
@patch.object(time, 'time')
|
||||||
def test_run_javascript_sync_no_wait(mock_time, display_window_env, mock_settings):
|
def test_run_in_display_sync_no_wait(mock_time, display_window_env, mock_settings):
|
||||||
"""
|
"""
|
||||||
test a synced script is run on the webview and immediately returns a result
|
test a synced script is run on the webview and immediately returns a result
|
||||||
"""
|
"""
|
||||||
|
@ -411,7 +426,7 @@ def test_run_javascript_sync_no_wait(mock_time, display_window_env, mock_setting
|
||||||
display_window.webview.page.return_value = webengine_page
|
display_window.webview.page.return_value = webengine_page
|
||||||
|
|
||||||
# WHEN: javascript is requested to run
|
# WHEN: javascript is requested to run
|
||||||
result = display_window.run_javascript('javascript to execute', True)
|
result = display_window._run_javascript('javascript to execute', True)
|
||||||
|
|
||||||
# THEN: javascript should be run with no delay and return with the correct result
|
# THEN: javascript should be run with no delay and return with the correct result
|
||||||
assert result == 1234
|
assert result == 1234
|
||||||
|
@ -428,7 +443,7 @@ def test_fix_font_bold_windows(mocked_is_win, display_window_env, mock_settings)
|
||||||
mocked_is_win.return_value = True
|
mocked_is_win.return_value = True
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.is_display = True
|
display_window.is_display = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
font_name = 'Arial Rounded MT Bold'
|
font_name = 'Arial Rounded MT Bold'
|
||||||
|
|
||||||
# WHEN: The font is processed
|
# WHEN: The font is processed
|
||||||
|
@ -447,7 +462,7 @@ def test_fix_font_bold_not_windows(mocked_is_win, display_window_env, mock_setti
|
||||||
mocked_is_win.return_value = False
|
mocked_is_win.return_value = False
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.is_display = True
|
display_window.is_display = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
font_name = 'Arial Rounded MT Bold'
|
font_name = 'Arial Rounded MT Bold'
|
||||||
|
|
||||||
# WHEN: The font is processed
|
# WHEN: The font is processed
|
||||||
|
@ -466,7 +481,7 @@ def test_fix_font_foundry(mocked_is_win, display_window_env, mock_settings):
|
||||||
mocked_is_win.return_value = False
|
mocked_is_win.return_value = False
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.is_display = True
|
display_window.is_display = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
font_name = 'CMG Sans [Foundry]'
|
font_name = 'CMG Sans [Foundry]'
|
||||||
|
|
||||||
# WHEN: The font is processed
|
# WHEN: The font is processed
|
||||||
|
@ -483,7 +498,7 @@ def test_set_theme_is_display_video(display_window_env, mock_settings, mock_geom
|
||||||
# GIVEN: A display window and a video theme
|
# GIVEN: A display window and a video theme
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.is_display = True
|
display_window.is_display = True
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
theme = Theme()
|
theme = Theme()
|
||||||
theme.background_type = 'video'
|
theme.background_type = 'video'
|
||||||
result_theme = Theme()
|
result_theme = Theme()
|
||||||
|
@ -494,8 +509,7 @@ def test_set_theme_is_display_video(display_window_env, mock_settings, mock_geom
|
||||||
display_window.set_theme(theme, is_sync=False, service_item_type=ServiceItemType.Text)
|
display_window.set_theme(theme, is_sync=False, service_item_type=ServiceItemType.Text)
|
||||||
|
|
||||||
# THEN: The final theme should be transparent
|
# THEN: The final theme should be transparent
|
||||||
display_window.run_javascript.assert_called_once_with('Display.setTheme({theme});'.format(theme=result_theme),
|
display_window.run_in_display.assert_called_once_with('setTheme', raw_parameters=result_theme, is_sync=False)
|
||||||
is_sync=False)
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_theme_not_display_video(display_window_env, mock_settings, mock_geometry):
|
def test_set_theme_not_display_video(display_window_env, mock_settings, mock_geometry):
|
||||||
|
@ -505,7 +519,7 @@ def test_set_theme_not_display_video(display_window_env, mock_settings, mock_geo
|
||||||
# GIVEN: A display window and a video theme
|
# GIVEN: A display window and a video theme
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.is_display = False
|
display_window.is_display = False
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
theme = Theme()
|
theme = Theme()
|
||||||
theme.background_type = 'video'
|
theme.background_type = 'video'
|
||||||
theme.background_border_color = 'border_colour'
|
theme.background_border_color = 'border_colour'
|
||||||
|
@ -523,8 +537,7 @@ def test_set_theme_not_display_video(display_window_env, mock_settings, mock_geo
|
||||||
display_window.set_theme(theme, is_sync=False, service_item_type=False)
|
display_window.set_theme(theme, is_sync=False, service_item_type=False)
|
||||||
|
|
||||||
# THEN: The final theme should use 'border_colour' for it's colour values
|
# THEN: The final theme should use 'border_colour' for it's colour values
|
||||||
display_window.run_javascript.assert_called_once_with('Display.setTheme({theme});'.format(theme=result_theme),
|
display_window.run_in_display.assert_called_once_with('setTheme', raw_parameters=result_theme, is_sync=False)
|
||||||
is_sync=False)
|
|
||||||
|
|
||||||
|
|
||||||
def test_set_theme_not_display_live(display_window_env, mock_settings, mock_geometry):
|
def test_set_theme_not_display_live(display_window_env, mock_settings, mock_geometry):
|
||||||
|
@ -534,7 +547,7 @@ def test_set_theme_not_display_live(display_window_env, mock_settings, mock_geom
|
||||||
# GIVEN: A display window and a video theme
|
# GIVEN: A display window and a video theme
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.is_display = False
|
display_window.is_display = False
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
theme = Theme()
|
theme = Theme()
|
||||||
theme.background_type = 'live'
|
theme.background_type = 'live'
|
||||||
result_theme = Theme()
|
result_theme = Theme()
|
||||||
|
@ -549,8 +562,7 @@ def test_set_theme_not_display_live(display_window_env, mock_settings, mock_geom
|
||||||
display_window.set_theme(theme, is_sync=False, service_item_type=False)
|
display_window.set_theme(theme, is_sync=False, service_item_type=False)
|
||||||
|
|
||||||
# THEN: The final theme should use the preset colour values
|
# THEN: The final theme should use the preset colour values
|
||||||
display_window.run_javascript.assert_called_once_with('Display.setTheme({theme});'.format(theme=result_theme),
|
display_window.run_in_display.assert_called_once_with('setTheme', raw_parameters=result_theme, is_sync=False)
|
||||||
is_sync=False)
|
|
||||||
|
|
||||||
|
|
||||||
@patch('openlp.core.display.window.Registry.execute')
|
@patch('openlp.core.display.window.Registry.execute')
|
||||||
|
@ -564,7 +576,7 @@ def test_show_display(mocked_screenlist, mocked_registry_execute, display_window
|
||||||
display_window.is_display = True
|
display_window.is_display = True
|
||||||
display_window.isHidden = MagicMock(return_value=True)
|
display_window.isHidden = MagicMock(return_value=True)
|
||||||
display_window.setVisible = MagicMock()
|
display_window.setVisible = MagicMock()
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
mocked_screenlist.screens = [1, 2]
|
mocked_screenlist.screens = [1, 2]
|
||||||
|
|
||||||
# WHEN: Show display is run
|
# WHEN: Show display is run
|
||||||
|
@ -572,7 +584,7 @@ def test_show_display(mocked_screenlist, mocked_registry_execute, display_window
|
||||||
|
|
||||||
# THEN: Should show the display and set the hide mode to none
|
# THEN: Should show the display and set the hide mode to none
|
||||||
display_window.setVisible.assert_called_once_with(True)
|
display_window.setVisible.assert_called_once_with(True)
|
||||||
display_window.run_javascript.assert_called_once_with('Display.show();')
|
display_window.run_in_display.assert_called_once_with('show')
|
||||||
|
|
||||||
|
|
||||||
@patch('openlp.core.display.window.ScreenList')
|
@patch('openlp.core.display.window.ScreenList')
|
||||||
|
@ -582,7 +594,7 @@ def test_show_display_no_display(mocked_screenlist, display_window_env, mock_set
|
||||||
"""
|
"""
|
||||||
# GIVEN: A Display window, one screen and core/display on monitor disabled
|
# GIVEN: A Display window, one screen and core/display on monitor disabled
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
display_window.is_display = True
|
display_window.is_display = True
|
||||||
mocked_screenlist.return_value = [1]
|
mocked_screenlist.return_value = [1]
|
||||||
mock_settings.value.return_value = False
|
mock_settings.value.return_value = False
|
||||||
|
@ -591,7 +603,7 @@ def test_show_display_no_display(mocked_screenlist, display_window_env, mock_set
|
||||||
display_window.show_display()
|
display_window.show_display()
|
||||||
|
|
||||||
# THEN: Shouldn't run the js show fn
|
# THEN: Shouldn't run the js show fn
|
||||||
assert display_window.run_javascript.call_count == 0
|
assert display_window.run_in_display.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
def test_hide_display_to_screen(display_window_env, mock_settings):
|
def test_hide_display_to_screen(display_window_env, mock_settings):
|
||||||
|
@ -600,7 +612,7 @@ def test_hide_display_to_screen(display_window_env, mock_settings):
|
||||||
"""
|
"""
|
||||||
# GIVEN: Display window and setting advanced/disable transparent display = False
|
# GIVEN: Display window and setting advanced/disable transparent display = False
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
display_window.setVisible = MagicMock()
|
display_window.setVisible = MagicMock()
|
||||||
mock_settings.value.return_value = False
|
mock_settings.value.return_value = False
|
||||||
|
|
||||||
|
@ -609,7 +621,7 @@ def test_hide_display_to_screen(display_window_env, mock_settings):
|
||||||
|
|
||||||
# THEN: Should hide the display with the js transparency function (not setVisible)
|
# THEN: Should hide the display with the js transparency function (not setVisible)
|
||||||
display_window.setVisible.call_count == 0
|
display_window.setVisible.call_count == 0
|
||||||
display_window.run_javascript.assert_called_once_with('Display.toTransparent();')
|
display_window.run_in_display.assert_called_once_with('toTransparent')
|
||||||
|
|
||||||
|
|
||||||
def test_hide_display_to_blank(display_window_env, mock_settings):
|
def test_hide_display_to_blank(display_window_env, mock_settings):
|
||||||
|
@ -618,14 +630,14 @@ def test_hide_display_to_blank(display_window_env, mock_settings):
|
||||||
"""
|
"""
|
||||||
# GIVEN: Display window and setting advanced/disable transparent display = False
|
# GIVEN: Display window and setting advanced/disable transparent display = False
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
mock_settings.value.return_value = False
|
mock_settings.value.return_value = False
|
||||||
|
|
||||||
# WHEN: Hide display is run with HideMode.Blank
|
# WHEN: Hide display is run with HideMode.Blank
|
||||||
display_window.hide_display(HideMode.Blank)
|
display_window.hide_display(HideMode.Blank)
|
||||||
|
|
||||||
# THEN: Should run the correct javascript on the display and set the hide mode
|
# THEN: Should run the correct javascript on the display and set the hide mode
|
||||||
display_window.run_javascript.assert_called_once_with('Display.toBlack();')
|
display_window.run_in_display.assert_called_once_with('toBlack')
|
||||||
|
|
||||||
|
|
||||||
def test_hide_display_to_theme(display_window_env, mock_settings):
|
def test_hide_display_to_theme(display_window_env, mock_settings):
|
||||||
|
@ -634,14 +646,14 @@ def test_hide_display_to_theme(display_window_env, mock_settings):
|
||||||
"""
|
"""
|
||||||
# GIVEN: Display window and setting advanced/disable transparent display = False
|
# GIVEN: Display window and setting advanced/disable transparent display = False
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
mock_settings.value.return_value = False
|
mock_settings.value.return_value = False
|
||||||
|
|
||||||
# WHEN: Hide display is run with HideMode.Theme
|
# WHEN: Hide display is run with HideMode.Theme
|
||||||
display_window.hide_display(HideMode.Theme)
|
display_window.hide_display(HideMode.Theme)
|
||||||
|
|
||||||
# THEN: Should run the correct javascript on the display and set the hide mode
|
# THEN: Should run the correct javascript on the display and set the hide mode
|
||||||
display_window.run_javascript.assert_called_once_with('Display.toTheme();')
|
display_window.run_in_display.assert_called_once_with('toTheme')
|
||||||
|
|
||||||
|
|
||||||
def test_hide_display_to_transparent(display_window_env, mock_settings):
|
def test_hide_display_to_transparent(display_window_env, mock_settings):
|
||||||
|
@ -650,7 +662,7 @@ def test_hide_display_to_transparent(display_window_env, mock_settings):
|
||||||
"""
|
"""
|
||||||
# GIVEN: Display window and setting advanced/disable transparent display = False
|
# GIVEN: Display window and setting advanced/disable transparent display = False
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.run_javascript = MagicMock()
|
display_window.run_in_display = MagicMock()
|
||||||
display_window.setVisible = MagicMock()
|
display_window.setVisible = MagicMock()
|
||||||
mock_settings.value.return_value = False
|
mock_settings.value.return_value = False
|
||||||
|
|
||||||
|
@ -658,7 +670,7 @@ def test_hide_display_to_transparent(display_window_env, mock_settings):
|
||||||
display_window.hide_display(HideMode.Screen)
|
display_window.hide_display(HideMode.Screen)
|
||||||
|
|
||||||
# THEN: Should run the correct javascript on the display and not set the visiblity
|
# THEN: Should run the correct javascript on the display and not set the visiblity
|
||||||
display_window.run_javascript.assert_called_once_with('Display.toTransparent();')
|
display_window.run_in_display.assert_called_once_with('toTransparent')
|
||||||
assert display_window.setVisible.call_count == 0
|
assert display_window.setVisible.call_count == 0
|
||||||
|
|
||||||
|
|
||||||
|
@ -774,12 +786,27 @@ def test_display_watcher_unregisters_registered_permanent_and_transient_event(di
|
||||||
event_listener_permanent.assert_not_called()
|
event_listener_permanent.assert_not_called()
|
||||||
|
|
||||||
|
|
||||||
|
def test_display_watcher_generates_event_names(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that the display watcher generate unique event names
|
||||||
|
"""
|
||||||
|
# GIVEN: Display window
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
|
||||||
|
# WHEN: Getting unique event names
|
||||||
|
first_event_name = display_window.display_watcher.get_unique_event_name()
|
||||||
|
second_event_name = display_window.display_watcher.get_unique_event_name()
|
||||||
|
|
||||||
|
# THEN: Event names should be different
|
||||||
|
assert first_event_name != second_event_name
|
||||||
|
|
||||||
|
|
||||||
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, setting advanced/disable transparent display = True and mocked run_javascript
|
# GIVEN: Display window, setting advanced/disable transparent display = True and mocked run_in_display
|
||||||
display_window = DisplayWindow()
|
display_window = DisplayWindow()
|
||||||
display_window.setVisible = MagicMock()
|
display_window.setVisible = MagicMock()
|
||||||
has_ran_event = False
|
has_ran_event = False
|
||||||
|
@ -788,11 +815,11 @@ def test_hide_transparent_to_screen(display_window_env, mock_settings):
|
||||||
nonlocal has_ran_event
|
nonlocal has_ran_event
|
||||||
has_ran_event = True
|
has_ran_event = True
|
||||||
|
|
||||||
def on_dispatch_event(_):
|
def on_dispatch_event(*args, **kwargs):
|
||||||
display_window.display_watcher.register_event_listener(TRANSITION_END_EVENT_NAME, set_has_ran_event, False)
|
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.display_watcher.dispatchEvent(TRANSITION_END_EVENT_NAME, {})
|
||||||
|
|
||||||
display_window.run_javascript = MagicMock(side_effect=on_dispatch_event)
|
display_window.run_in_display = 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
|
||||||
|
@ -887,3 +914,79 @@ def test_close_event_accepts_event_manual_close(display_window_env, mock_setting
|
||||||
|
|
||||||
# THEN: The event should have been ignored
|
# THEN: The event should have been ignored
|
||||||
assert mocked_event.ignore.called is False
|
assert mocked_event.ignore.called is False
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_in_display_run(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that when run_in_display is called
|
||||||
|
"""
|
||||||
|
# GIVEN: A DisplayWindow instance and a mocked _run_javascript
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window._run_javascript = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: The run_is_display is called
|
||||||
|
display_window.run_in_display('test_event')
|
||||||
|
|
||||||
|
# THEN: The event should be called
|
||||||
|
display_window._run_javascript.assert_called_once_with('requestAction(\'test_event\')', False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_in_display_honors_is_sync(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that when run_in_display honors is_sync flag
|
||||||
|
"""
|
||||||
|
# GIVEN: A DisplayWindow instance and a mocked _run_javascript
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window._run_javascript = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: The run_is_display is called
|
||||||
|
display_window.run_in_display('test_event', is_sync=True)
|
||||||
|
|
||||||
|
# THEN: The event should be called
|
||||||
|
display_window._run_javascript.assert_called_once_with('requestAction(\'test_event\')', True)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_in_display_honors_raw_parameters(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that when run_in_display honors raw_parameters parameters
|
||||||
|
"""
|
||||||
|
# GIVEN: A DisplayWindow instance and a mocked _run_javascript
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window._run_javascript = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: The run_is_display is called
|
||||||
|
display_window.run_in_display('test_event', raw_parameters='a test: testing')
|
||||||
|
|
||||||
|
# THEN: The event should be called
|
||||||
|
display_window._run_javascript.assert_called_once_with('requestAction(\'test_event\', a test: testing)', False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_in_display_honors_return_event_name(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that when run_in_display honors return_event_name parameter
|
||||||
|
"""
|
||||||
|
# GIVEN: A DisplayWindow instance and a mocked _run_javascript
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window._run_javascript = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: The run_is_display is called
|
||||||
|
display_window.run_in_display('test_event', return_event_name='test_event')
|
||||||
|
|
||||||
|
# THEN: The event should be called
|
||||||
|
display_window._run_javascript.assert_called_once_with('requestActionAsync(\'test_event\', \'test_event\')', False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_run_in_display_dumps_json(display_window_env, mock_settings):
|
||||||
|
"""
|
||||||
|
Test that when run_in_display is called with parameters, each of it will be dumped as a JSON string
|
||||||
|
"""
|
||||||
|
# GIVEN: A DisplayWindow instance and a mocked _run_javascript
|
||||||
|
display_window = DisplayWindow()
|
||||||
|
display_window._run_javascript = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: The run_is_display is called
|
||||||
|
display_window.run_in_display('test_event', 1.23, 'a "string', [1, 2, 'test'], {"test1": "test2"})
|
||||||
|
|
||||||
|
# THEN: The parameters should be correctly converted to JSON
|
||||||
|
display_window._run_javascript.assert_called_once_with('requestAction(\'test_event\', 1.23, "a \\"string\", '
|
||||||
|
'[1, 2, "test"], {"test1": "test2"})', False)
|
||||||
|
|
Loading…
Reference in New Issue