forked from openlp/openlp
"Make sure the main display on Mac OS X stays above the menu bar and dock but still allow the main window to be focused"
bzr-revno: 2585
This commit is contained in:
commit
b87156e104
@ -317,6 +317,8 @@ class GeneralTab(SettingsTab):
|
|||||||
self.custom_Y_value_edit.value(),
|
self.custom_Y_value_edit.value(),
|
||||||
self.custom_width_value_edit.value(),
|
self.custom_width_value_edit.value(),
|
||||||
self.custom_height_value_edit.value())
|
self.custom_height_value_edit.value())
|
||||||
|
self.screens.override['number'] = self.screens.which_screen(self.screens.override['size'])
|
||||||
|
self.screens.override['primary'] = (self.screens.desktop.primaryScreen() == self.screens.override['number'])
|
||||||
if self.override_radio_button.isChecked():
|
if self.override_radio_button.isChecked():
|
||||||
self.screens.set_override_display()
|
self.screens.set_override_display()
|
||||||
else:
|
else:
|
||||||
|
@ -39,6 +39,13 @@ from openlp.core.lib import ServiceItem, ImageSource, ScreenList, build_html, ex
|
|||||||
from openlp.core.lib.theme import BackgroundType
|
from openlp.core.lib.theme import BackgroundType
|
||||||
from openlp.core.ui import HideMode, AlertLocation
|
from openlp.core.ui import HideMode, AlertLocation
|
||||||
|
|
||||||
|
if is_macosx():
|
||||||
|
from ctypes import pythonapi, c_void_p, c_char_p, py_object
|
||||||
|
|
||||||
|
from sip import voidptr
|
||||||
|
from objc import objc_object
|
||||||
|
from AppKit import NSMainMenuWindowLevel, NSWindowCollectionBehaviorManaged
|
||||||
|
|
||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
OPAQUE_STYLESHEET = """
|
OPAQUE_STYLESHEET = """
|
||||||
@ -154,15 +161,30 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
|||||||
# regressions on other platforms.
|
# regressions on other platforms.
|
||||||
if is_macosx():
|
if is_macosx():
|
||||||
window_flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Window
|
window_flags = QtCore.Qt.FramelessWindowHint | QtCore.Qt.Window
|
||||||
# For primary screen ensure it stays above the OS X dock
|
|
||||||
# and menu bar
|
|
||||||
if self.screens.current['primary']:
|
|
||||||
self.setWindowState(QtCore.Qt.WindowFullScreen)
|
|
||||||
else:
|
|
||||||
window_flags |= QtCore.Qt.WindowStaysOnTopHint
|
|
||||||
self.setWindowFlags(window_flags)
|
self.setWindowFlags(window_flags)
|
||||||
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
self.setAttribute(QtCore.Qt.WA_DeleteOnClose)
|
||||||
self.set_transparency(False)
|
self.set_transparency(False)
|
||||||
|
if is_macosx():
|
||||||
|
if self.is_live:
|
||||||
|
# Get a pointer to the underlying NSView
|
||||||
|
try:
|
||||||
|
nsview_pointer = self.winId().ascapsule()
|
||||||
|
except:
|
||||||
|
nsview_pointer = voidptr(self.winId()).ascapsule()
|
||||||
|
# Set PyCapsule name so pyobjc will accept it
|
||||||
|
pythonapi.PyCapsule_SetName.restype = c_void_p
|
||||||
|
pythonapi.PyCapsule_SetName.argtypes = [py_object, c_char_p]
|
||||||
|
pythonapi.PyCapsule_SetName(nsview_pointer, c_char_p(b"objc.__object__"))
|
||||||
|
# Covert the NSView pointer into a pyobjc NSView object
|
||||||
|
self.pyobjc_nsview = objc_object(cobject=nsview_pointer)
|
||||||
|
# Set the window level so that the MainDisplay is above the menu bar and dock
|
||||||
|
self.pyobjc_nsview.window().setLevel_(NSMainMenuWindowLevel + 2)
|
||||||
|
# Set the collection behavior so the window is visible when Mission Control is activated
|
||||||
|
self.pyobjc_nsview.window().setCollectionBehavior_(NSWindowCollectionBehaviorManaged)
|
||||||
|
if self.screens.current['primary']:
|
||||||
|
# Connect focusWindowChanged signal so we can change the window level when the display is not in
|
||||||
|
# focus on the primary screen
|
||||||
|
self.application.focusWindowChanged.connect(self.change_window_level)
|
||||||
if self.is_live:
|
if self.is_live:
|
||||||
Registry().register_function('live_display_hide', self.hide_display)
|
Registry().register_function('live_display_hide', self.hide_display)
|
||||||
Registry().register_function('live_display_show', self.show_display)
|
Registry().register_function('live_display_show', self.show_display)
|
||||||
@ -186,6 +208,12 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
|||||||
Remove registered function on close.
|
Remove registered function on close.
|
||||||
"""
|
"""
|
||||||
if self.is_live:
|
if self.is_live:
|
||||||
|
if is_macosx():
|
||||||
|
# Block signals so signal we are disconnecting can't get called while we disconnect it
|
||||||
|
self.blockSignals(True)
|
||||||
|
if self.screens.current['primary']:
|
||||||
|
self.application.focusWindowChanged.disconnect()
|
||||||
|
self.blockSignals(False)
|
||||||
Registry().remove_function('live_display_hide', self.hide_display)
|
Registry().remove_function('live_display_hide', self.hide_display)
|
||||||
Registry().remove_function('live_display_show', self.show_display)
|
Registry().remove_function('live_display_show', self.show_display)
|
||||||
Registry().remove_function('update_display_css', self.css_changed)
|
Registry().remove_function('update_display_css', self.css_changed)
|
||||||
@ -500,6 +528,36 @@ class MainDisplay(OpenLPMixin, Display, RegistryProperties):
|
|||||||
self.setCursor(QtCore.Qt.ArrowCursor)
|
self.setCursor(QtCore.Qt.ArrowCursor)
|
||||||
self.frame.evaluateJavaScript('document.body.style.cursor = "auto"')
|
self.frame.evaluateJavaScript('document.body.style.cursor = "auto"')
|
||||||
|
|
||||||
|
def change_window_level(self, window):
|
||||||
|
"""
|
||||||
|
Changes the display window level on Mac OS X so that the main window can be brought into focus but still allow
|
||||||
|
the main display to be above the menu bar and dock when it in focus.
|
||||||
|
|
||||||
|
:param window: Window from our application that focus changed to or None if outside our application
|
||||||
|
"""
|
||||||
|
if is_macosx():
|
||||||
|
if window:
|
||||||
|
# Get different window ids' as int's
|
||||||
|
try:
|
||||||
|
window_id = window.winId().__int__()
|
||||||
|
main_window_id = self.main_window.winId().__int__()
|
||||||
|
self_id = self.winId().__int__()
|
||||||
|
except:
|
||||||
|
return
|
||||||
|
# If the passed window has the same id as our window make sure the display has the proper level and
|
||||||
|
# collection behavior.
|
||||||
|
if window_id == self_id:
|
||||||
|
self.pyobjc_nsview.window().setLevel_(NSMainMenuWindowLevel + 2)
|
||||||
|
self.pyobjc_nsview.window().setCollectionBehavior_(NSWindowCollectionBehaviorManaged)
|
||||||
|
# Else set the displays window level back to normal since we are trying to focus a window other than
|
||||||
|
# the display.
|
||||||
|
else:
|
||||||
|
self.pyobjc_nsview.window().setLevel_(0)
|
||||||
|
self.pyobjc_nsview.window().setCollectionBehavior_(NSWindowCollectionBehaviorManaged)
|
||||||
|
# If we are trying to focus the main window raise it now to complete the focus change.
|
||||||
|
if window_id == main_window_id:
|
||||||
|
self.main_window.raise_()
|
||||||
|
|
||||||
|
|
||||||
class AudioPlayer(OpenLPMixin, QtCore.QObject):
|
class AudioPlayer(OpenLPMixin, QtCore.QObject):
|
||||||
"""
|
"""
|
||||||
|
@ -41,6 +41,7 @@ except ImportError:
|
|||||||
|
|
||||||
IS_WIN = sys.platform.startswith('win')
|
IS_WIN = sys.platform.startswith('win')
|
||||||
IS_LIN = sys.platform.startswith('lin')
|
IS_LIN = sys.platform.startswith('lin')
|
||||||
|
IS_MAC = sys.platform.startswith('dar')
|
||||||
|
|
||||||
|
|
||||||
VERS = {
|
VERS = {
|
||||||
@ -66,6 +67,11 @@ LINUX_MODULES = [
|
|||||||
'dbus',
|
'dbus',
|
||||||
]
|
]
|
||||||
|
|
||||||
|
MACOSX_MODULES = [
|
||||||
|
'objc',
|
||||||
|
'AppKit'
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
MODULES = [
|
MODULES = [
|
||||||
'PyQt5',
|
'PyQt5',
|
||||||
@ -234,6 +240,10 @@ def main():
|
|||||||
print('Checking for Linux specific modules...')
|
print('Checking for Linux specific modules...')
|
||||||
for m in LINUX_MODULES:
|
for m in LINUX_MODULES:
|
||||||
check_module(m)
|
check_module(m)
|
||||||
|
elif IS_MAC:
|
||||||
|
print('Checking for Mac OS X specific modules...')
|
||||||
|
for m in MACOSX_MODULES:
|
||||||
|
check_module(m)
|
||||||
verify_versions()
|
verify_versions()
|
||||||
print_qt_image_formats()
|
print_qt_image_formats()
|
||||||
print_enchant_backends_and_languages()
|
print_enchant_backends_and_languages()
|
||||||
|
@ -26,7 +26,7 @@ from unittest import TestCase
|
|||||||
|
|
||||||
from PyQt5 import QtCore
|
from PyQt5 import QtCore
|
||||||
|
|
||||||
from openlp.core.common import Registry
|
from openlp.core.common import Registry, is_macosx
|
||||||
from openlp.core.lib import ScreenList
|
from openlp.core.lib import ScreenList
|
||||||
from openlp.core.ui import MainDisplay
|
from openlp.core.ui import MainDisplay
|
||||||
from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
|
from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
|
||||||
@ -34,6 +34,13 @@ from openlp.core.ui.maindisplay import TRANSPARENT_STYLESHEET, OPAQUE_STYLESHEET
|
|||||||
from tests.helpers.testmixin import TestMixin
|
from tests.helpers.testmixin import TestMixin
|
||||||
from tests.functional import MagicMock, patch
|
from tests.functional import MagicMock, patch
|
||||||
|
|
||||||
|
if is_macosx():
|
||||||
|
from ctypes import pythonapi, c_void_p, c_char_p, py_object
|
||||||
|
|
||||||
|
from sip import voidptr
|
||||||
|
from objc import objc_object
|
||||||
|
from AppKit import NSMainMenuWindowLevel, NSWindowCollectionBehaviorManaged
|
||||||
|
|
||||||
|
|
||||||
class TestMainDisplay(TestCase, TestMixin):
|
class TestMainDisplay(TestCase, TestMixin):
|
||||||
|
|
||||||
@ -135,31 +142,11 @@ class TestMainDisplay(TestCase, TestMixin):
|
|||||||
mocked_bibles_plugin.refresh_css.assert_called_with(main_display.frame)
|
mocked_bibles_plugin.refresh_css.assert_called_with(main_display.frame)
|
||||||
|
|
||||||
@patch('openlp.core.ui.maindisplay.is_macosx')
|
@patch('openlp.core.ui.maindisplay.is_macosx')
|
||||||
def macosx_non_primary_screen_window_flags_state_test(self, is_macosx):
|
def macosx_display_window_flags_state_test(self, is_macosx):
|
||||||
"""
|
"""
|
||||||
Test that on Mac OS X when the current screen isn't primary we set the proper window flags and window state
|
Test that on Mac OS X we set the proper window flags
|
||||||
"""
|
"""
|
||||||
# GIVEN: A new SlideController instance on Mac OS X with the current display not being primary.
|
# GIVEN: A new SlideController instance on Mac OS X.
|
||||||
is_macosx.return_value = True
|
|
||||||
self.screens.set_current_display(1)
|
|
||||||
display = MagicMock()
|
|
||||||
|
|
||||||
# WHEN: The default controller is built.
|
|
||||||
main_display = MainDisplay(display)
|
|
||||||
|
|
||||||
# THEN: The window flags and state should be the same as those needed on Mac OS X for the non primary display.
|
|
||||||
self.assertEqual(QtCore.Qt.WindowStaysOnTopHint | QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint,
|
|
||||||
main_display.windowFlags(),
|
|
||||||
'The window flags should be Qt.WindowStaysOnTop, Qt.Window, and Qt.FramelessWindowHint.')
|
|
||||||
self.assertNotEqual(QtCore.Qt.WindowFullScreen, main_display.windowState(),
|
|
||||||
'The window state should not be full screen.')
|
|
||||||
|
|
||||||
@patch('openlp.core.ui.maindisplay.is_macosx')
|
|
||||||
def macosx_primary_screen_window_flags_state_test(self, is_macosx):
|
|
||||||
"""
|
|
||||||
Test that on Mac OS X when the current screen is primary we set the proper window flags and window state
|
|
||||||
"""
|
|
||||||
# GIVEN: A new SlideController instance on Mac OS X with the current display being primary.
|
|
||||||
is_macosx.return_value = True
|
is_macosx.return_value = True
|
||||||
self.screens.set_current_display(0)
|
self.screens.set_current_display(0)
|
||||||
display = MagicMock()
|
display = MagicMock()
|
||||||
@ -167,8 +154,34 @@ class TestMainDisplay(TestCase, TestMixin):
|
|||||||
# WHEN: The default controller is built.
|
# WHEN: The default controller is built.
|
||||||
main_display = MainDisplay(display)
|
main_display = MainDisplay(display)
|
||||||
|
|
||||||
# THEN: The window flags and state should be the same as those needed on Mac OS X for the primary display.
|
# THEN: The window flags should be the same as those needed on Mac OS X.
|
||||||
self.assertEqual(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint, main_display.windowFlags(),
|
self.assertEqual(QtCore.Qt.Window | QtCore.Qt.FramelessWindowHint,
|
||||||
'The window flags should be Qt.Window and Qt.FramelessWindowHint.')
|
main_display.windowFlags(),
|
||||||
self.assertEqual(QtCore.Qt.WindowFullScreen, main_display.windowState(),
|
'The window flags should be Qt.Window, and Qt.FramelessWindowHint.')
|
||||||
'The window state should be full screen.')
|
|
||||||
|
def macosx_display_test(self):
|
||||||
|
"""
|
||||||
|
Test display on Mac OS X
|
||||||
|
"""
|
||||||
|
if not is_macosx():
|
||||||
|
self.skipTest('Can only run test on Mac OS X due to pyobjc dependency.')
|
||||||
|
# GIVEN: A new SlideController instance on Mac OS X.
|
||||||
|
self.screens.set_current_display(0)
|
||||||
|
display = MagicMock()
|
||||||
|
|
||||||
|
# WHEN: The default controller is built and a reference to the underlying NSView is stored.
|
||||||
|
main_display = MainDisplay(display)
|
||||||
|
try:
|
||||||
|
nsview_pointer = main_display.winId().ascapsule()
|
||||||
|
except:
|
||||||
|
nsview_pointer = voidptr(main_display.winId()).ascapsule()
|
||||||
|
pythonapi.PyCapsule_SetName.restype = c_void_p
|
||||||
|
pythonapi.PyCapsule_SetName.argtypes = [py_object, c_char_p]
|
||||||
|
pythonapi.PyCapsule_SetName(nsview_pointer, c_char_p(b"objc.__object__"))
|
||||||
|
pyobjc_nsview = objc_object(cobject=nsview_pointer)
|
||||||
|
|
||||||
|
# THEN: The window level and collection behavior should be the same as those needed for Mac OS X.
|
||||||
|
self.assertEqual(pyobjc_nsview.window().level(), NSMainMenuWindowLevel + 2,
|
||||||
|
'Window level should be NSMainMenuWindowLevel + 2')
|
||||||
|
self.assertEqual(pyobjc_nsview.window().collectionBehavior(), NSWindowCollectionBehaviorManaged,
|
||||||
|
'Window collection behavior should be NSWindowCollectionBehaviorManaged')
|
||||||
|
Loading…
Reference in New Issue
Block a user