This commit is contained in:
Tim 2019-11-01 15:56:34 +00:00
parent 2d2576aa6c
commit 57dd0897ae
No known key found for this signature in database
GPG Key ID: 3D454289AF831A6D
25 changed files with 797 additions and 118 deletions

View File

@ -1,17 +1,16 @@
stages:
- lint
- test
- deploy
lint-python:
stage: lint
stage: test
image: openlp/debian
script:
- sh scripts/generate_resources.sh
- flake8
lint-javascript:
stage: lint
stage: test
image: openlp/angular
script:
- yarn install

View File

@ -43,6 +43,26 @@ var VerticalAlign = {
Bottom: 2
};
/**
* Transition type enumeration
*/
var TransitionType = {
Fade: 0,
Slide: 1,
Convex: 2,
Concave: 3,
Zoom: 4
};
/**
* Transition speed enumeration
*/
var TransitionSpeed = {
Normal: 0,
Fast: 1,
Slow: 2
};
/**
* Audio state enumeration
*/
@ -329,6 +349,7 @@ var Display = {
_alertState: AlertState.NotDisplaying,
_transitionState: TransitionState.NoTransition,
_animationState: AnimationState.NoAnimation,
_doTransitions: false,
_revealConfig: {
margin: 0.0,
minScale: 1.0,
@ -348,21 +369,25 @@ var Display = {
/**
* Start up reveal and do any other initialisation
*/
init: function () {
init: function (doTransitions=false) {
Display._doTransitions = doTransitions;
Reveal.initialize(Display._revealConfig);
},
/**
* Reinitialise Reveal
*/
reinit: function () {
Reveal.reinitialize();
Reveal.sync();
// Python expects to be on first page after reinit
Reveal.slide(0);
},
/**
* Set the transition type
* @param {string} transitionType - Can be one of "none", "fade", "slide", "convex", "concave", "zoom"
* @param {string} transitionSpeed - Can be one of "default", "fast", "slow"
*/
setTransition: function (transitionType) {
Reveal.configure({"transition": transitionType});
setTransition: function (transitionType, transitionSpeed) {
Reveal.configure({"transition": transitionType, "transitionSpeed": transitionSpeed});
},
/**
* Clear the current list of slides
@ -639,7 +664,6 @@ var Display = {
Display.addTextSlide(slide.verse, slide.text, slide.footer, false);
});
Display.reinit();
Display.goToSlide(0);
},
/**
* Create the <section> that will contain text slides (vertical slides in react)
@ -869,6 +893,44 @@ var Display = {
},
setTheme: function (theme) {
Display._theme = theme;
// Set slide transitions
var new_transition_type = "none",
new_transition_speed = "default";
if (!!theme.display_slide_transition && Display._doTransitions) {
switch (theme.display_slide_transition_type) {
case TransitionType.Fade:
new_transition_type = "fade";
break;
case TransitionType.Slide:
new_transition_type = "slide";
break;
case TransitionType.Convex:
new_transition_type = "convex";
break;
case TransitionType.Concave:
new_transition_type = "concave";
break;
case TransitionType.Zoom:
new_transition_type = "zoom";
break;
default:
new_transition_type = "fade";
}
switch (theme.display_slide_transition_speed) {
case TransitionSpeed.Normal:
new_transition_speed = "default";
break;
case TransitionSpeed.Fast:
new_transition_speed = "fast";
break;
case TransitionSpeed.Slow:
new_transition_speed = "slow";
break;
default:
new_transition_speed = "default";
}
}
Display.setTransition(new_transition_type, new_transition_speed);
// Set the background
var globalBackground = $("#global-background")[0];
var backgroundStyle = {};
@ -986,7 +1048,7 @@ var Display = {
default:
mainStyle["justify-content"] = "center";
}
if (theme.hasOwnProperty('font_main_shadow_size')) {
if (theme.hasOwnProperty('font_main_shadow_size') && !!theme.font_main_shadow) {
mainStyle["text-shadow"] = theme.font_main_shadow_color + " " + theme.font_main_shadow_size + "pt " +
theme.font_main_shadow_size + "pt";
}

View File

@ -441,14 +441,6 @@
}
/**
* Restarts up the presentation if the client is capable.
*/
function reinitialize() {
initialized = false;
initialize(config);
}
/**
* Inspect the client to see what it's capable of, this
* should only happens once per runtime.
@ -5814,7 +5806,6 @@
VERSION: VERSION,
initialize: initialize,
reinitialize: reinitialize,
configure: configure,
sync: sync,

View File

@ -109,13 +109,17 @@ class DisplayWindow(QtWidgets.QWidget):
Create the display window
"""
super(DisplayWindow, self).__init__(parent)
# Gather all flags for the display window
flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint
if Settings().value('advanced/x11 bypass wm'):
flags |= QtCore.Qt.X11BypassWindowManagerHint
# Need to import this inline to get around a QtWebEngine issue
from openlp.core.display.webengine import WebEngineView
self._is_initialised = False
self._can_show_startup_screen = can_show_startup_screen
self._fbo = None
self.setWindowTitle(translate('OpenLP.DisplayWindow', 'Display Window'))
self.setWindowFlags(QtCore.Qt.FramelessWindowHint | QtCore.Qt.Tool | QtCore.Qt.WindowStaysOnTopHint)
self.setWindowFlags(flags)
self.setAttribute(QtCore.Qt.WA_TranslucentBackground)
self.setAutoFillBackground(True)
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
@ -198,13 +202,13 @@ class DisplayWindow(QtWidgets.QWidget):
"""
Add stuff after page initialisation
"""
self.run_javascript('Display.init();')
js_is_display = str(self.is_display).lower()
self.run_javascript('Display.init({do_transitions});'.format(do_transitions=js_is_display))
self._is_initialised = True
if self._can_show_startup_screen:
self.set_startup_screen()
# Make sure the scale is set if it was attempted set before init
if self.scale != 1:
self.set_scale(self.scale)
if self._can_show_startup_screen:
self.set_startup_screen()
def run_javascript(self, script, is_sync=False):
"""

View File

@ -11,6 +11,8 @@
"display" :{
"horizontal_align": 0,
"slide_transition": false,
"slide_transition_type": 0,
"slide_transition_speed": 0,
"vertical_align": 0
},
"font": {

View File

@ -127,6 +127,82 @@ class BackgroundGradientType(object):
return BackgroundGradientType.LeftBottom
class TransitionType(object):
"""
Type enumeration for transition types.
"""
Fade = 0
Slide = 1
Convex = 2
Concave = 3
Zoom = 4
@staticmethod
def to_string(transition_type):
"""
Return a string representation of a transition type.
"""
if transition_type == TransitionType.Fade:
return 'fade'
elif transition_type == TransitionType.Slide:
return 'slide'
elif transition_type == TransitionType.Convex:
return 'convex'
elif transition_type == TransitionType.Concave:
return 'concave'
elif transition_type == TransitionType.Zoom:
return 'zoom'
@staticmethod
def from_string(type_string):
"""
Return a transition type for the given string.
"""
if type_string == 'fade':
return TransitionType.Fade
elif type_string == 'slide':
return TransitionType.Slide
elif type_string == 'convex':
return TransitionType.Convex
elif type_string == 'concave':
return TransitionType.Concave
elif type_string == 'zoom':
return TransitionType.Zoom
class TransitionSpeed(object):
"""
Type enumeration for transition types.
"""
Normal = 0
Fast = 1
Slow = 2
@staticmethod
def to_string(transition_speed):
"""
Return a string representation of a transition type.
"""
if transition_speed == TransitionSpeed.Normal:
return 'normal'
elif transition_speed == TransitionSpeed.Fast:
return 'fast'
elif transition_speed == TransitionSpeed.Slow:
return 'slow'
@staticmethod
def from_string(type_string):
"""
Return a transition type for the given string.
"""
if type_string == 'normal':
return TransitionSpeed.Normal
if type_string == 'fast':
return TransitionSpeed.Fast
elif type_string == 'slow':
return TransitionSpeed.Slow
class HorizontalType(object):
"""
Type enumeration for horizontal alignment.
@ -153,7 +229,7 @@ class VerticalType(object):
BOOLEAN_LIST = ['bold', 'italics', 'override', 'outline', 'shadow', 'slide_transition']
INTEGER_LIST = ['size', 'line_adjustment', 'x', 'height', 'y', 'width', 'shadow_size', 'outline_size',
'horizontal_align', 'vertical_align', 'wrap_style']
'horizontal_align', 'vertical_align', 'wrap_style', 'slide_transition_type', 'slide_transition_speed']
class Theme(object):
@ -204,12 +280,21 @@ class Theme(object):
Set the header and footer size into the current primary screen.
10 px on each side is removed to allow for a border.
"""
self.set_default_header()
self.set_default_footer()
def set_default_header(self):
current_screen_geometry = ScreenList().current.display_geometry
self.font_main_x = 10
self.font_main_y = 0
self.font_main_width = current_screen_geometry.width() - 20
self.font_main_height = current_screen_geometry.height() * 9 / 10
self.font_footer_width = current_screen_geometry.width() - 20
def set_default_footer(self):
current_screen_geometry = ScreenList().current.display_geometry
self.font_footer_x = 10
self.font_footer_y = current_screen_geometry.height() * 9 / 10
self.font_footer_width = current_screen_geometry.width() - 20
self.font_footer_height = current_screen_geometry.height() / 10
def load_theme(self, theme, theme_path=None):

View File

@ -62,6 +62,7 @@ class UiIcons(metaclass=Singleton):
'authentication': {'icon': 'fa.exclamation-triangle', 'attr': 'red'},
'address': {'icon': 'fa.book'},
'back': {'icon': 'fa.step-backward'},
'backspace': {'icon': 'mdi.backspace-outline'},
'bible': {'icon': 'fa.book'},
'blank': {'icon': 'fa.times-circle'},
'blank_theme': {'icon': 'fa.file-image-o'},

View File

@ -670,6 +670,10 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.application.process_events()
plugin.first_time()
self.application.process_events()
# Load the themes from files
self.theme_manager_contents.load_first_time_themes()
# Update the theme widget
self.theme_manager_contents.load_themes()
temp_path = Path(gettempdir(), 'openlp')
shutil.rmtree(temp_path, True)
@ -714,10 +718,6 @@ class MainWindow(QtWidgets.QMainWindow, Ui_MainWindow, LogMixin, RegistryPropert
self.active_plugin.toggle_status(PluginStatus.Inactive)
# Set global theme and
Registry().execute('theme_update_global')
# Load the themes from files
self.theme_manager_contents.load_first_time_themes()
# Update the theme widget
self.theme_manager_contents.load_themes()
# Check if any Bibles downloaded. If there are, they will be processed.
Registry().execute('bibles_load_list')
self.application.set_normal_cursor()

View File

@ -323,16 +323,23 @@ class ShortcutListForm(QtWidgets.QDialog, Ui_ShortcutListDialog, RegistryPropert
if not toggled:
return
action = self._current_item_action()
shortcuts = self._action_shortcuts(action)
self.refresh_shortcut_list()
primary_button_text = ''
alternate_button_text = ''
if shortcuts:
primary_button_text = self.get_shortcut_string(shortcuts[0], for_display=True)
if len(shortcuts) == 2:
alternate_button_text = self.get_shortcut_string(shortcuts[1], for_display=True)
self.primary_push_button.setText(primary_button_text)
self.alternate_push_button.setText(alternate_button_text)
if action is None:
QtWidgets.QMessageBox.information(self, translate('OpenLP.ShortcutListForm', 'Select an Action'),
translate('OpenLP.ShortcutListForm', 'Select an action and click one '
'of the buttons below to start '
'capturing a new primary or alternate shortcut, respectively.'))
else:
shortcuts = self._action_shortcuts(action)
self.refresh_shortcut_list()
primary_button_text = ''
alternate_button_text = ''
if shortcuts:
primary_button_text = self.get_shortcut_string(shortcuts[0], for_display=True)
if len(shortcuts) == 2:
alternate_button_text = self.get_shortcut_string(shortcuts[1], for_display=True)
self.primary_push_button.setText(primary_button_text)
self.alternate_push_button.setText(alternate_button_text)
def save(self):
"""

View File

@ -98,6 +98,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
self.main_font_combo_box.activated.connect(self.calculate_lines)
self.footer_font_combo_box.activated.connect(self.update_theme)
self.footer_size_spin_box.valueChanged.connect(self.update_theme)
self.transitions_check_box.stateChanged.connect(self.on_transitions_check_box_state_changed)
def set_defaults(self):
"""
@ -145,6 +146,8 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
self.background_page.registerField('horizontal', self.horizontal_combo_box)
self.background_page.registerField('vertical', self.vertical_combo_box)
self.background_page.registerField('slide_transition', self.transitions_check_box)
self.background_page.registerField('slide_transition_type', self.transition_combo_box)
self.background_page.registerField('slide_transition_speed', self.transition_speed_combo_box)
self.background_page.registerField('name', self.theme_name_edit)
def calculate_lines(self):
@ -251,10 +254,7 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
Change state as Shadow check box changed
"""
if self.update_theme_allowed:
if state == QtCore.Qt.Checked:
self.theme.font_main_shadow = True
else:
self.theme.font_main_shadow = False
self.theme.font_main_shadow = state == QtCore.Qt.Checked
self.shadow_color_button.setEnabled(self.theme.font_main_shadow)
self.shadow_size_spin_box.setEnabled(self.theme.font_main_shadow)
self.calculate_lines()
@ -275,6 +275,16 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
if self.update_theme_allowed:
self.theme.font_footer_override = (value != QtCore.Qt.Checked)
def on_transitions_check_box_state_changed(self, state):
"""
Change state as Transitions check box is changed
"""
if self.update_theme_allowed:
self.theme.display_slide_transition = state == QtCore.Qt.Checked
self.transition_combo_box.setEnabled(self.theme.display_slide_transition)
self.transition_speed_combo_box.setEnabled(self.theme.display_slide_transition)
self.calculate_lines()
def exec(self, edit=False):
"""
Run the wizard.
@ -395,6 +405,8 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
self.setField('horizontal', self.theme.display_horizontal_align)
self.setField('vertical', self.theme.display_vertical_align)
self.setField('slide_transition', self.theme.display_slide_transition)
self.setField('slide_transition_type', self.theme.display_slide_transition_type)
self.setField('slide_transition_speed', self.theme.display_slide_transition_speed)
def set_preview_page_values(self):
"""
@ -525,19 +537,28 @@ class ThemeForm(QtWidgets.QWizard, Ui_ThemeWizard, RegistryProperties):
# footer page
self.theme.font_footer_name = self.footer_font_combo_box.currentFont().family()
self.theme.font_footer_size = self.field('footer_size_spin_box')
# position page
self.theme.font_main_x = self.field('main_position_x')
self.theme.font_main_y = self.field('main_position_y')
self.theme.font_main_height = self.field('main_position_height')
self.theme.font_main_width = self.field('main_position_width')
self.theme.font_footer_x = self.field('footer_position_x')
self.theme.font_footer_y = self.field('footer_position_y')
self.theme.font_footer_height = self.field('footer_position_height')
self.theme.font_footer_width = self.field('footer_position_width')
# position page (main)
if self.theme.font_main_override:
self.theme.font_main_x = self.field('main_position_x')
self.theme.font_main_y = self.field('main_position_y')
self.theme.font_main_height = self.field('main_position_height')
self.theme.font_main_width = self.field('main_position_width')
else:
self.theme.set_default_header()
# position page (footer)
if self.theme.font_footer_override:
self.theme.font_footer_x = self.field('footer_position_x')
self.theme.font_footer_y = self.field('footer_position_y')
self.theme.font_footer_height = self.field('footer_position_height')
self.theme.font_footer_width = self.field('footer_position_width')
else:
self.theme.set_default_footer()
# position page
self.theme.display_horizontal_align = self.horizontal_combo_box.currentIndex()
self.theme.display_vertical_align = self.vertical_combo_box.currentIndex()
self.theme.display_slide_transition = self.field('slide_transition')
self.theme.display_slide_transition_type = self.field('slide_transition_type')
self.theme.display_slide_transition_speed = self.field('slide_transition_speed')
def accept(self):
"""

View File

@ -149,16 +149,15 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
process the bootstrap initialise setup request
"""
self.setup_ui(self)
self.progress_form = ThemeProgressForm(self)
self.global_theme = Settings().value(self.settings_section + '/global theme')
self.build_theme_path()
self.load_first_time_themes()
self.upgrade_themes() # TODO: Can be removed when upgrade path from OpenLP 2.4 no longer needed
def bootstrap_post_set_up(self):
"""
process the bootstrap post setup request
"""
self.progress_form = ThemeProgressForm(self)
self.theme_form = ThemeForm(self)
self.theme_form.path = self.theme_path
self.file_rename_form = FileRenameForm()
@ -481,7 +480,8 @@ class ThemeManager(QtWidgets.QWidget, RegistryBase, Ui_ThemeManager, LogMixin, R
self.save_theme(theme)
Settings().setValue(self.settings_section + '/global theme', theme.theme_name)
new_themes = [theme.theme_name]
self.update_preview_images(new_themes)
if new_themes:
self.update_preview_images(new_themes)
self.application.set_normal_cursor()
def load_themes(self):

View File

@ -36,6 +36,8 @@ class ThemeProgressForm(QtWidgets.QDialog, UiThemeProgressDialog, RegistryProper
super().__init__(parent)
self.setup_ui(self)
self._theme_list = []
def show(self):
self.progress_bar.setMinimum(0)
self.progress_bar.setMaximum(0)
self.progress_bar.setValue(0)
@ -45,6 +47,7 @@ class ThemeProgressForm(QtWidgets.QDialog, UiThemeProgressDialog, RegistryProper
except ZeroDivisionError:
self.ratio = 16 / 9
self.theme_preview_layout.aspect_ratio = self.ratio
return super().show()
def get_preview(self, theme_name, theme_data):
self.label.setText(theme_name)

View File

@ -25,7 +25,13 @@ from PyQt5 import QtCore, QtGui, QtWidgets
from openlp.core.common import is_macosx
from openlp.core.common.i18n import UiStrings, translate
from openlp.core.lib.theme import BackgroundGradientType, BackgroundType, HorizontalType
from openlp.core.lib.theme import (
BackgroundGradientType,
BackgroundType,
HorizontalType,
TransitionType,
TransitionSpeed
)
from openlp.core.lib.ui import add_welcome_page, create_valign_selection_widgets
from openlp.core.ui.icons import UiIcons
from openlp.core.widgets.buttons import ColorButton
@ -271,12 +277,22 @@ class Ui_ThemeWizard(object):
self.vertical_label.setObjectName('vertical_label')
self.vertical_combo_box.setObjectName('vertical_combo_box')
self.alignment_layout.addRow(self.vertical_label, self.vertical_combo_box)
self.transitions_label = QtWidgets.QLabel(self.alignment_page)
self.transitions_label.setObjectName('transitions_label')
self.transitions_check_box = QtWidgets.QCheckBox(self.alignment_page)
self.transitions_check_box.setObjectName('transitions_check_box')
self.alignment_layout.addRow(self.transitions_label, self.transitions_check_box)
self.alignment_layout.setItem(3, QtWidgets.QFormLayout.LabelRole, self.spacer)
self.transition_layout = QtWidgets.QHBoxLayout()
self.transition_layout.setObjectName("transition_layout")
self.transition_combo_box = QtWidgets.QComboBox(self.alignment_page)
self.transition_combo_box.setObjectName("transition_combo_box")
self.transition_combo_box.addItems(['', '', '', '', ''])
self.transition_layout.addWidget(self.transition_combo_box)
self.transition_speed_label = QtWidgets.QLabel(self.alignment_page)
self.transition_speed_label.setObjectName("transition_speed_label")
self.transition_layout.addWidget(self.transition_speed_label)
self.transition_speed_combo_box = QtWidgets.QComboBox(self.alignment_page)
self.transition_speed_combo_box.setObjectName("transition_speed_combo_box")
self.transition_speed_combo_box.addItems(['', '', ''])
self.transition_layout.addWidget(self.transition_speed_combo_box)
self.alignment_layout.addRow(self.transitions_check_box, self.transition_layout)
theme_wizard.addPage(self.alignment_page)
# Area Position Page
self.area_position_page = QtWidgets.QWizardPage()
@ -460,7 +476,16 @@ class Ui_ThemeWizard(object):
self.horizontal_combo_box.setItemText(HorizontalType.Right, translate('OpenLP.ThemeWizard', 'Right'))
self.horizontal_combo_box.setItemText(HorizontalType.Center, translate('OpenLP.ThemeWizard', 'Center'))
self.horizontal_combo_box.setItemText(HorizontalType.Justify, translate('OpenLP.ThemeWizard', 'Justify'))
self.transitions_label.setText(translate('OpenLP.ThemeWizard', 'Transitions:'))
self.transitions_check_box.setText(translate('OpenLP.ThemeWizard', 'Transitions:'))
self.transition_combo_box.setItemText(TransitionType.Fade, translate('OpenLP.ThemeWizard', 'Fade'))
self.transition_combo_box.setItemText(TransitionType.Slide, translate('OpenLP.ThemeWizard', 'Slide'))
self.transition_combo_box.setItemText(TransitionType.Concave, translate('OpenLP.ThemeWizard', 'Concave'))
self.transition_combo_box.setItemText(TransitionType.Convex, translate('OpenLP.ThemeWizard', 'Convex'))
self.transition_combo_box.setItemText(TransitionType.Zoom, translate('OpenLP.ThemeWizard', 'Zoom'))
self.transition_speed_label.setText(translate('OpenLP.ThemeWizard', 'Speed:'))
self.transition_speed_combo_box.setItemText(TransitionSpeed.Normal, translate('OpenLP.ThemeWizard', 'Normal'))
self.transition_speed_combo_box.setItemText(TransitionSpeed.Fast, translate('OpenLP.ThemeWizard', 'Fast'))
self.transition_speed_combo_box.setItemText(TransitionSpeed.Slow, translate('OpenLP.ThemeWizard', 'Slow'))
self.area_position_page.setTitle(translate('OpenLP.ThemeWizard', 'Output Area Locations'))
self.area_position_page.setSubTitle(translate('OpenLP.ThemeWizard', 'Allows you to change and move the'
' Main and Footer areas.'))

View File

@ -64,7 +64,7 @@ class SearchEdit(QtWidgets.QLineEdit):
self.settings_section = settings_section
self._current_search_type = -1
self.clear_button = QtWidgets.QToolButton(self)
self.clear_button.setIcon(UiIcons().shortcuts)
self.clear_button.setIcon(UiIcons().backspace)
self.clear_button.setCursor(QtCore.Qt.ArrowCursor)
self.clear_button.setStyleSheet('QToolButton { border: none; padding: 0px; }')
self.clear_button.resize(18, 18)

View File

@ -765,13 +765,72 @@ p, li { white-space: pre-wrap; }
</item>
</widget>
</item>
<item row="2" column="1">
<item row="2" column="0">
<widget class="QCheckBox" name="transitionsCheckBox">
<property name="text">
<string>Transitions</string>
<string>Transitions:</string>
</property>
</widget>
</item>
<item row="2" column="1">
<layout class="QHBoxLayout" name="transitionLayout">
<item>
<widget class="QComboBox" name="transitionComboBox">
<item>
<property name="text">
<string>Fade</string>
</property>
</item>
<item>
<property name="text">
<string>Slide</string>
</property>
</item>
<item>
<property name="text">
<string>Convex</string>
</property>
</item>
<item>
<property name="text">
<string>Concave</string>
</property>
</item>
<item>
<property name="text">
<string>Zoom</string>
</property>
</item>
</widget>
</item>
<item>
<widget class="QLabel" name="transitionSpeedLabel">
<property name="text">
<string>Speed:</string>
</property>
</widget>
</item>
<item>
<widget class="QComboBox" name="transitionSpeedComboBox">
<item>
<property name="text">
<string>Normal</string>
</property>
</item>
<item>
<property name="text">
<string>Fast</string>
</property>
</item>
<item>
<property name="text">
<string>Slow</string>
</property>
</item>
</widget>
</item>
</layout>
</item>
</layout>
</widget>
<widget class="QWizardPage" name="areaPositionPage">

View File

@ -1,25 +0,0 @@
--- reveal.js.orig 2018-08-01 10:37:51.000000000 +0200
+++ reveal.js 2019-02-11 21:25:39.396198927 +0100
@@ -383,6 +383,14 @@
}
/**
+ * Restarts up the presentation if the client is capable.
+ */
+ function reinitialize() {
+ initialized = false;
+ initialize(config);
+ }
+
+ /**
* Inspect the client to see what it's capable of, this
* should only happens once per runtime.
*/
@@ -5372,6 +5380,7 @@
VERSION: VERSION,
initialize: initialize,
+ reinitialize: reinitialize,
configure: configure,
sync: sync,

View File

@ -144,6 +144,23 @@ class TestInit(TestCase, TestMixin):
# THEN: the result will be as expected - try again
assert str(value) == 'called'
def test_requires_auth_missing_credentials(self):
"""
Test the requires_auth wrapper with enabled security and authorization taken place and and error
:return:
"""
# GIVEN: An enabled security and a known user
Settings().setValue('api/authentication enabled', True)
Settings().setValue('api/user id', 'superfly')
Settings().setValue('api/password', 'lamas')
# WHEN: I call the function with no password
wrapped_function = requires_auth(func)
value = wrapped_function(0)
# THEN: the result will be as expected (unauthorized)
assert str(value) == str(authenticate())
def func(field=None):
return 'called'

View File

@ -0,0 +1,76 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.display.window package.
"""
import sys
from unittest import TestCase
from unittest.mock import MagicMock, patch
from PyQt5 import QtCore
# Mock QtWebEngineWidgets
sys.modules['PyQt5.QtWebEngineWidgets'] = MagicMock()
from openlp.core.display.window import DisplayWindow
from tests.helpers.testmixin import TestMixin
@patch('PyQt5.QtWidgets.QVBoxLayout')
@patch('openlp.core.display.webengine.WebEngineView')
@patch('openlp.core.display.window.Settings')
class TestDisplayWindow(TestCase, TestMixin):
"""
A test suite to test the functions in DisplayWindow
"""
def test_x11_override_on(self, MockSettings, mocked_webengine, mocked_addWidget):
"""
Test that the x11 override option bit is set
"""
# GIVEN: x11 bypass is on
mocked_settings = MagicMock()
mocked_settings.value.return_value = True
MockSettings.return_value = mocked_settings
# WHEN: A DisplayWindow is generated
display_window = DisplayWindow()
# THEN: The x11 override flag should be set
x11_bit = display_window.windowFlags() & QtCore.Qt.X11BypassWindowManagerHint
assert x11_bit == QtCore.Qt.X11BypassWindowManagerHint
def test_x11_override_off(self, MockSettings, mocked_webengine, mocked_addWidget):
"""
Test that the x11 override option bit is not set when setting if off
"""
# GIVEN: x11 bypass is off
mocked_settings = MagicMock()
mocked_settings.value.return_value = False
MockSettings.return_value = mocked_settings
# WHEN: A DisplayWindow is generated
display_window = DisplayWindow()
# THEN: The x11 override flag should not be set
x11_bit = display_window.windowFlags() & QtCore.Qt.X11BypassWindowManagerHint
assert x11_bit != QtCore.Qt.X11BypassWindowManagerHint

View File

@ -23,6 +23,7 @@ Package to test the openlp.core.lib.theme package.
"""
from pathlib import Path
from unittest import TestCase
from unittest.mock import MagicMock, patch
from openlp.core.lib.theme import BackgroundType, Theme
@ -175,10 +176,83 @@ class TestTheme(TestCase):
lt.load_theme(save_theme_json)
self.check_theme(lt)
@patch('openlp.core.display.screens.ScreenList.current')
def test_set_default_footer(self, mock_geometry):
"""
Test the set_default_footer function sets the footer back to default
(reletive to the screen)
"""
# GIVEN: A screen geometry object and a Theme footer with a strange area
mock_geometry.display_geometry = MagicMock()
mock_geometry.display_geometry.height.return_value = 600
mock_geometry.display_geometry.width.return_value = 400
theme = Theme()
theme.font_main_x = 20
theme.font_footer_x = 207
theme.font_footer_y = 25
theme.font_footer_width = 4253
theme.font_footer_height = 5423
# WHEN: set_default_footer is called
theme.set_default_footer()
# THEN: footer should be set, header should not have changed
assert theme.font_main_x == 20, 'header should not have been changed'
assert theme.font_footer_x == 10, 'x pos should be reset to default of 10'
assert theme.font_footer_y == 540, 'y pos should be reset to (screen_size_height * 9 / 10)'
assert theme.font_footer_width == 380, 'width should have been reset to (screen_size_width - 20)'
assert theme.font_footer_height == 60, 'height should have been reset to (screen_size_height / 10)'
@patch('openlp.core.display.screens.ScreenList.current')
def test_set_default_header(self, mock_geometry):
"""
Test the set_default_header function sets the header back to default
(reletive to the screen)
"""
# GIVEN: A screen geometry object and a Theme header with a strange area
mock_geometry.display_geometry = MagicMock()
mock_geometry.display_geometry.height.return_value = 600
mock_geometry.display_geometry.width.return_value = 400
theme = Theme()
theme.font_footer_x = 200
theme.font_main_x = 687
theme.font_main_y = 546
theme.font_main_width = 345
theme.font_main_height = 653
# WHEN: set_default_header is called
theme.set_default_header()
# THEN: footer should be set, header should not have changed
assert theme.font_footer_x == 200, 'footer should not have been changed'
assert theme.font_main_x == 10, 'x pos should be reset to default of 10'
assert theme.font_main_y == 0, 'y pos should be reset to 0'
assert theme.font_main_width == 380, 'width should have been reset to (screen_size_width - 20)'
assert theme.font_main_height == 540, 'height should have been reset to (screen_size_height * 9 / 10)'
@patch('openlp.core.display.screens.ScreenList.current')
def test_set_default_header_footer(self, mock_geometry):
"""
Test the set_default_header_footer function sets the header and footer back to default
(reletive to the screen)
"""
# GIVEN: A screen geometry object and a Theme header with a strange area
mock_geometry.display_geometry = MagicMock()
theme = Theme()
theme.font_footer_x = 200
theme.font_main_x = 687
# WHEN: set_default_header is called
theme.set_default_header_footer()
# THEN: footer should be set, header should not have changed
assert theme.font_footer_x == 10, 'footer x pos should be reset to default of 10'
assert theme.font_main_x == 10, 'header x pos should be reset to default of 10'
def check_theme(self, theme):
assert '#000000' == theme.background_border_color, 'background_border_color should be "#000000"'
assert 'solid' == theme.background_type, 'background_type should be "solid"'
assert 0 == theme.display_vertical_align, 'display_vertical_align should be 0'
assert theme.font_footer_bold is False, 'font_footer_bold should be False'
assert 'Arial' == theme.font_main_name, 'font_main_name should be "Arial"'
assert 49 == len(theme.__dict__), 'The theme should have 49 attributes'
assert 51 == len(theme.__dict__), 'The theme should have 51 attributes'

View File

@ -941,10 +941,8 @@ class TestInfoLabel(TestCase):
"""
Test the paintEvent method when text fits the label
"""
font = QtGui.QFont()
metrics = QtGui.QFontMetrics(font)
with patch('openlp.core.ui.slidecontroller.QtWidgets.QLabel'), \
patch('openlp.core.ui.slidecontroller.QtGui.QFontMetrics') as MockFontMetrics, \
patch('openlp.core.ui.slidecontroller.QtGui.QPainter') as mocked_qpainter:
# GIVEN: An instance of InfoLabel, with mocked text return, width and rect methods
@ -957,9 +955,11 @@ class TestInfoLabel(TestCase):
info_label.rect = mocked_rect
info_label.text = mocked_text
info_label.width = mocked_width
mocked_font_metrics = MagicMock()
mocked_font_metrics.elidedText.return_value = test_string
MockFontMetrics.return_value = mocked_font_metrics
# WHEN: The instance is wider than its text, and the paintEvent method is called
info_label.width.return_value = metrics.boundingRect(test_string).width() + 10
info_label.paintEvent(MagicMock())
# THEN: The text should be drawn centered with the complete test_string
@ -969,15 +969,14 @@ class TestInfoLabel(TestCase):
"""
Test the paintEvent method when text fits the label
"""
font = QtGui.QFont()
metrics = QtGui.QFontMetrics(font)
with patch('openlp.core.ui.slidecontroller.QtWidgets.QLabel'), \
patch('openlp.core.ui.slidecontroller.QtGui.QFontMetrics') as MockFontMetrics, \
patch('openlp.core.ui.slidecontroller.QtGui.QPainter') as mocked_qpainter:
# GIVEN: An instance of InfoLabel, with mocked text return, width and rect methods
info_label = InfoLabel()
test_string = 'Label Text'
elided_test_string = test_string[0:5] + '...'
mocked_rect = MagicMock()
mocked_text = MagicMock()
mocked_width = MagicMock()
@ -985,14 +984,14 @@ class TestInfoLabel(TestCase):
info_label.rect = mocked_rect
info_label.text = mocked_text
info_label.width = mocked_width
mocked_font_metrics = MagicMock()
mocked_font_metrics.elidedText.return_value = elided_test_string
MockFontMetrics.return_value = mocked_font_metrics
# WHEN: The instance is narrower than its text, and the paintEvent method is called
label_width = metrics.boundingRect(test_string).width() - 10
info_label.width.return_value = label_width
info_label.paintEvent(MagicMock())
# THEN: The text should be drawn aligned left with an elided test_string
elided_test_string = metrics.elidedText(test_string, QtCore.Qt.ElideRight, label_width)
mocked_qpainter().drawText.assert_called_once_with(mocked_rect(), QtCore.Qt.AlignLeft, elided_test_string)
@patch('builtins.super')

View File

@ -0,0 +1,46 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.ui package.
"""
from PyQt5 import QtCore
from openlp.core.ui.splashscreen import SplashScreen
class TestSplashScreen():
"""
This will test the SplachScreen.py file
"""
def test_SplashScreen(self):
"""
Test that the SpashScreen is created correctly
"""
# GIVEN: the SplashScreen class
# WHEN: An object is created
ss = SplashScreen()
# THEN: Nothing should go wrong and the instance should have the correct values
assert ss.objectName() == 'splashScreen', 'The ObjectName should have be ' \
'splashScreen'
assert ss.frameSize() == QtCore.QSize(370, 370), 'The frameSize should be (370, 370)'
assert ss.contextMenuPolicy() == QtCore.Qt.PreventContextMenu, 'The ContextMenuPolicy ' \
'should have been QtCore.Qt.PreventContextMenu or 4'

View File

@ -28,9 +28,9 @@ from unittest.mock import MagicMock, patch
from openlp.core.ui.themeform import ThemeForm
class TestThemeManager(TestCase):
class TestThemeForm(TestCase):
"""
Test the functions in the ThemeManager Class
Test the functions in the ThemeForm Class
"""
def setUp(self):
with patch('openlp.core.ui.themeform.ThemeForm._setup'):

View File

@ -58,19 +58,16 @@ class TestThemeManager(TestCase, TestMixin):
# GIVEN: A new a call to initialise
self.theme_manager.setup_ui = MagicMock()
self.theme_manager.build_theme_path = MagicMock()
self.theme_manager.load_first_time_themes = MagicMock()
self.theme_manager.upgrade_themes = MagicMock()
Settings().setValue('themes/global theme', 'my_theme')
# WHEN: the initialisation is run
with patch('openlp.core.ui.thememanager.ThemeProgressForm'):
self.theme_manager.bootstrap_initialise()
self.theme_manager.bootstrap_initialise()
# THEN:
self.theme_manager.setup_ui.assert_called_once_with(self.theme_manager)
assert self.theme_manager.global_theme == 'my_theme'
self.theme_manager.build_theme_path.assert_called_once_with()
self.theme_manager.load_first_time_themes.assert_called_once_with()
self.theme_manager.upgrade_themes.assert_called_once_with()
@patch('openlp.core.ui.thememanager.create_paths')
@ -117,7 +114,8 @@ class TestThemeManager(TestCase, TestMixin):
self.theme_manager.theme_path = MagicMock()
# WHEN:
self.theme_manager.bootstrap_post_set_up()
with patch('openlp.core.ui.thememanager.ThemeProgressForm'):
self.theme_manager.bootstrap_post_set_up()
# THEN:
assert 1 == self.theme_manager.load_themes.call_count, "load_themes should have been called once"

View File

@ -113,22 +113,18 @@ describe("The Display object", function () {
expect(Display.reinit).toBeDefined();
});
it("should re-initialise Reveal when reinit is called", function () {
spyOn(Reveal, "reinitialize");
it("should sync Reveal and set to first slide when reinit is called", function () {
spyOn(Reveal, "sync");
spyOn(Reveal, "slide");
Display.reinit();
expect(Reveal.reinitialize).toHaveBeenCalled();
expect(Reveal.sync).toHaveBeenCalled();
expect(Reveal.slide).toHaveBeenCalledWith(0);
});
it("should have a setTransition() method", function () {
expect(Display.setTransition).toBeDefined();
});
it("should have a correctly functioning setTransition() method", function () {
spyOn(Reveal, "configure");
Display.setTransition("fade");
expect(Reveal.configure).toHaveBeenCalledWith({"transition": "fade"});
});
it("should have a correctly functioning clearSlides() method", function () {
expect(Display.clearSlides).toBeDefined();
@ -156,6 +152,55 @@ describe("The Display object", function () {
});
describe("Transitions", function () {
beforeEach(function() {
document.body.innerHTML = "";
_createDiv({"class": "slides"});
_createDiv({"class": "footer"});
_createDiv({"id": "global-background"});
Display._slides = {};
});
afterEach(function() {
// Reset theme
Display._theme = null;
});
it("should have a correctly functioning setTransition() method", function () {
spyOn(Reveal, "configure");
Display.setTransition("fade", "slow");
expect(Reveal.configure).toHaveBeenCalledWith({"transition": "fade", "transitionSpeed": "slow"});
});
it("should have enabled transitions when _doTransitions is true and setTheme is run", function () {
spyOn(Display, "setTransition");
Display._doTransitions = true;
var theme = {
"display_slide_transition": true,
"display_slide_transition_type": TransitionType.Slide,
"display_slide_transition_speed": TransitionSpeed.Fast
}
Display.setTheme(theme);
expect(Display.setTransition).toHaveBeenCalledWith("slide", "fast");
});
it("should have not enabled transitions when init() with no transitions and setTheme is run", function () {
spyOn(Display, "setTransition");
Display._doTransitions = false;
var theme = {
"display_slide_transition": true,
"display_slide_transition_type": TransitionType.Slide,
"display_slide_transition_speed": TransitionSpeed.Fast,
}
Display.setTheme(theme);
expect(Display.setTransition).toHaveBeenCalledWith("none", "default");
});
});
describe("Display.alert", function () {
var alertContainer, alertBackground, alertText, settings, text;
@ -519,7 +564,6 @@ describe("Display.setTextSlides", function () {
];
spyOn(Display, "clearSlides");
spyOn(Display, "reinit");
spyOn(Reveal, "slide");
Display.setTextSlides(slides);
@ -528,7 +572,6 @@ describe("Display.setTextSlides", function () {
expect(Display._slides["v2"]).toEqual(1);
expect($(".slides > section > section").length).toEqual(2);
expect(Display.reinit).toHaveBeenCalledTimes(1);
expect(Reveal.slide).toHaveBeenCalledWith(0, 0);
});
it("should correctly set outline width", function () {
@ -583,6 +626,56 @@ describe("Display.setTextSlides", function () {
expect(slidesDiv.style['justify-content']).toEqual('center');
})
it("should enable shadows", function () {
const slides = [
{
"verse": "v1",
"text": "Amazing grace, how sweet the sound\nThat saved a wretch like me\n" +
"I once was lost, but now I'm found\nWas blind but now I see",
"footer": "Public Domain"
}
];
//
const theme = {
'font_main_shadow': true,
'font_main_shadow_color': "#000",
'font_main_shadow_size': 5
};
spyOn(Display, "reinit");
spyOn(Reveal, "slide");
Display.setTheme(theme);
Display.setTextSlides(slides);
const slidesDiv = $(".text-slides")[0];
expect(slidesDiv.style['text-shadow']).not.toEqual('');
})
it("should not enable shadows", function () {
const slides = [
{
"verse": "v1",
"text": "Amazing grace, how sweet the sound\nThat saved a wretch like me\n" +
"I once was lost, but now I'm found\nWas blind but now I see",
"footer": "Public Domain"
}
];
//
const theme = {
'font_main_shadow': false,
'font_main_shadow_color': "#000",
'font_main_shadow_size': 5
};
spyOn(Display, "reinit");
spyOn(Reveal, "slide");
Display.setTheme(theme);
Display.setTextSlides(slides);
const slidesDiv = $(".text-slides")[0];
expect(slidesDiv.style['text-shadow']).toEqual('');
})
it("should correctly set slide size position to theme size when adding a text slide", function () {
const slides = [
{

View File

@ -0,0 +1,142 @@
# -*- coding: utf-8 -*-
##########################################################################
# OpenLP - Open Source Lyrics Projection #
# ---------------------------------------------------------------------- #
# Copyright (c) 2008-2019 OpenLP Developers #
# ---------------------------------------------------------------------- #
# This program is free software: you can redistribute it and/or modify #
# it under the terms of the GNU General Public License as published by #
# the Free Software Foundation, either version 3 of the License, or #
# (at your option) any later version. #
# #
# This program is distributed in the hope that it will be useful, #
# but WITHOUT ANY WARRANTY; without even the implied warranty of #
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the #
# GNU General Public License for more details. #
# #
# You should have received a copy of the GNU General Public License #
# along with this program. If not, see <https://www.gnu.org/licenses/>. #
##########################################################################
"""
Package to test the openlp.core.ui.themeform package.
"""
# from pathlib import Path
from unittest import TestCase
from unittest.mock import MagicMock, patch
from openlp.core.ui.themeprogressform import ThemeProgressForm
from tests.helpers.testmixin import TestMixin
class TestThemeProgressForm(TestCase, TestMixin):
"""
Test the functions in the ThemeProgressForm class
"""
def setUp(self):
self.setup_application()
def tearDown(self):
del self.app
def _get_theme_progress_form(self):
"""Common code used to create the ThemeProgressForm object"""
with patch('openlp.core.ui.themeprogressdialog.ThemePreviewRenderer'), \
patch('openlp.core.ui.themeprogressdialog.UiThemeProgressDialog.setup_ui'):
form = ThemeProgressForm()
return form
def test_init(self):
"""Test that the ThemeProgressForm is created without problems"""
# GIVEN: ThemeProgressForm class
# WHEN: An object is instatiated
# THEN: There is no problem
self._get_theme_progress_form()
@patch('openlp.core.ui.themeprogressform.ScreenList')
@patch('openlp.core.ui.themeprogressform.QtWidgets.QDialog.show')
def test_show(self, mocked_show, MockScreenList):
"""Test that the ThemeProgressForm is created without problems"""
# GIVEN: ThemeProgressForm object
form = self._get_theme_progress_form()
mocked_screen_list = MagicMock()
mocked_screen_list.current.display_geometry.width.return_value = 1920
mocked_screen_list.current.display_geometry.height.return_value = 1080
MockScreenList.return_value = mocked_screen_list
form.progress_bar = MagicMock()
form.theme_preview_layout = MagicMock()
# WHEN: The show() method is called
form.show()
# THEN: The correct display ratio is calculated and the form is shown
expected_ratio = 16 / 9
form.progress_bar.setMinimum.assert_called_once_with(0)
form.progress_bar.setMaximum.assert_called_once_with(0)
form.progress_bar.setValue.assert_called_once_with(0)
assert form.ratio == expected_ratio
assert form.theme_preview_layout.aspect_ratio == expected_ratio
mocked_show.assert_called_once()
@patch('openlp.core.ui.themeprogressform.ScreenList')
@patch('openlp.core.ui.themeprogressform.QtWidgets.QDialog.show')
def test_show_divide_by_zero(self, mocked_show, MockScreenList):
"""Test that the ThemeProgressForm is created without problems even if there's a divide by zero exception"""
# GIVEN: ThemeProgressForm object
form = self._get_theme_progress_form()
mocked_screen_list = MagicMock()
mocked_screen_list.current.display_geometry.width.return_value = 1920
mocked_screen_list.current.display_geometry.height.return_value = 0
MockScreenList.return_value = mocked_screen_list
form.progress_bar = MagicMock()
form.theme_preview_layout = MagicMock()
# WHEN: The show() method is called
form.show()
# THEN: The correct display ratio is calculated and the form is shown
expected_ratio = 16 / 9
form.progress_bar.setMinimum.assert_called_once_with(0)
form.progress_bar.setMaximum.assert_called_once_with(0)
form.progress_bar.setValue.assert_called_once_with(0)
assert form.ratio == expected_ratio
assert form.theme_preview_layout.aspect_ratio == expected_ratio
mocked_show.assert_called_once()
def test_get_preview(self):
"""Test that the get_preview() method returns a preview image"""
# GIVEN: ThemeProgressForm object
test_theme_name = 'Test Theme'
test_theme_data = {'name': test_theme_name}
form = self._get_theme_progress_form()
form.progress_bar = MagicMock(**{'value.return_value': 0})
form.label = MagicMock()
form.theme_display = MagicMock(**{'width.return_value': 192, 'generate_preview.return_value': 'preview'})
form.renderer.width = MagicMock(return_value=1920)
# WHEN: get_preview() is called
preview = form.get_preview(test_theme_name, test_theme_data)
# THEN: All the correct methods should be called and the correct results should be returned
form.label.setText.assert_called_once_with(test_theme_name)
form.progress_bar.value.assert_called_once()
form.progress_bar.setValue.assert_called_once_with(1)
form.theme_display.width.assert_called_once()
form.renderer.width.assert_called_once()
form.theme_display.set_scale.assert_called_once_with(0.1)
form.theme_display.generate_preview.assert_called_once_with(test_theme_data, generate_screenshot=True)
assert preview == 'preview'
def test_theme_list(self):
# GIVEN: ThemeProgressForm object and theme list
test_theme_list = ['Theme 1', 'Theme 2']
form = self._get_theme_progress_form()
form.progress_bar = MagicMock()
# WHEN: theme_list is set and get'ed
form.theme_list = test_theme_list
theme_list = form.theme_list
# THEN: The theme list should be correct
form.progress_bar.setMaximum.assert_called_once_with(2)
assert theme_list == test_theme_list