mirror of
https://github.com/kovidgoyal/calibre.git
synced 2025-07-09 03:04:10 -04:00
Workaround for broken handling of menus on OSX in Qt 5
This commit is contained in:
parent
500b05da83
commit
8ea1a6c6cb
@ -359,6 +359,9 @@ class ChooseLibraryAction(InterfaceAction):
|
|||||||
self.gui.location_manager.set_switch_actions(quick_actions,
|
self.gui.location_manager.set_switch_actions(quick_actions,
|
||||||
rename_actions, delete_actions, qs_actions,
|
rename_actions, delete_actions, qs_actions,
|
||||||
self.action_choose)
|
self.action_choose)
|
||||||
|
# Allow the cloned actions in the OS X global menubar to update
|
||||||
|
for a in (self.qaction, self.menuless_qaction):
|
||||||
|
a.changed.emit()
|
||||||
|
|
||||||
def location_selected(self, loc):
|
def location_selected(self, loc):
|
||||||
enabled = loc == 'library'
|
enabled = loc == 'library'
|
||||||
|
@ -7,10 +7,11 @@ __license__ = 'GPL v3'
|
|||||||
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
__copyright__ = '2011, Kovid Goyal <kovid@kovidgoyal.net>'
|
||||||
__docformat__ = 'restructuredtext en'
|
__docformat__ = 'restructuredtext en'
|
||||||
|
|
||||||
|
import sip
|
||||||
from PyQt5.Qt import (Qt, QAction, QMenu, QMenuBar, QObject,
|
from PyQt5.Qt import (Qt, QAction, QMenu, QMenuBar, QObject,
|
||||||
QToolBar, QToolButton, QSize)
|
QToolBar, QToolButton, QSize, pyqtSignal, QTimer)
|
||||||
|
|
||||||
|
from calibre.constants import isosx
|
||||||
from calibre.gui2.throbber import create_donate_widget
|
from calibre.gui2.throbber import create_donate_widget
|
||||||
from calibre.gui2 import gprefs
|
from calibre.gui2 import gprefs
|
||||||
|
|
||||||
@ -204,61 +205,203 @@ class MenuAction(QAction): # {{{
|
|||||||
self.setText(self.clone.text())
|
self.setText(self.clone.text())
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
class MenuBar(QMenuBar): # {{{
|
# MenuBar {{{
|
||||||
|
|
||||||
def __init__(self, location_manager, parent):
|
if isosx:
|
||||||
QMenuBar.__init__(self, parent)
|
# On OS X we need special handling for the application global menu bar and
|
||||||
self.gui = parent
|
# the context menus, since Qt does not handle dynamic menus or menus in
|
||||||
self.setNativeMenuBar(True)
|
# which the same action occurs in more than one place.
|
||||||
|
|
||||||
self.location_manager = location_manager
|
class CloneAction(QAction):
|
||||||
self.added_actions = []
|
|
||||||
|
|
||||||
self.donate_action = QAction(_('Donate'), self)
|
text_changed = pyqtSignal()
|
||||||
self.donate_menu = QMenu()
|
visibility_changed = pyqtSignal()
|
||||||
self.donate_menu.addAction(self.gui.donate_action)
|
|
||||||
self.donate_action.setMenu(self.donate_menu)
|
|
||||||
|
|
||||||
def update_lm_actions(self):
|
def __init__(self, clone, parent, is_top_level=False, clone_shortcuts=True):
|
||||||
for ac in self.added_actions:
|
QAction.__init__(self, clone.text(), parent)
|
||||||
clone = getattr(ac, 'clone', None)
|
self.is_top_level = is_top_level
|
||||||
if clone is not None and clone in self.location_manager.all_actions:
|
self.clone_shortcuts = clone_shortcuts
|
||||||
ac.setVisible(clone in self.location_manager.available_actions)
|
self.clone = clone
|
||||||
|
clone.changed.connect(self.clone_changed)
|
||||||
|
self.clone_changed()
|
||||||
|
self.triggered.connect(self.do_trigger)
|
||||||
|
|
||||||
def init_bar(self, actions):
|
def clone_changed(self):
|
||||||
for ac in self.added_actions:
|
otext = self.text()
|
||||||
m = ac.menu()
|
self.setText(self.clone.text())
|
||||||
if m is not None:
|
if otext != self.text:
|
||||||
m.setVisible(False)
|
self.text_changed.emit()
|
||||||
|
ov = self.isVisible()
|
||||||
|
self.setVisible(self.clone.isVisible())
|
||||||
|
if ov != self.isVisible():
|
||||||
|
self.visibility_changed.emit()
|
||||||
|
self.setEnabled(self.clone.isEnabled())
|
||||||
|
self.setCheckable(self.clone.isCheckable())
|
||||||
|
self.setChecked(self.clone.isChecked())
|
||||||
|
self.setIcon(self.clone.icon())
|
||||||
|
if self.clone_shortcuts:
|
||||||
|
self.setShortcuts(self.clone.shortcuts())
|
||||||
|
if self.clone.menu() is None:
|
||||||
|
if not self.is_top_level:
|
||||||
|
self.setMenu(None)
|
||||||
|
else:
|
||||||
|
m = QMenu(self.text(), self.parent())
|
||||||
|
for ac in QMenu.actions(self.clone.menu()):
|
||||||
|
if ac.isSeparator():
|
||||||
|
m.addSeparator()
|
||||||
|
else:
|
||||||
|
m.addAction(CloneAction(ac, self.parent(), clone_shortcuts=self.clone_shortcuts))
|
||||||
|
self.setMenu(m)
|
||||||
|
|
||||||
self.clear()
|
def do_trigger(self, checked=False):
|
||||||
self.added_actions = []
|
if not sip.isdeleted(self.clone):
|
||||||
|
self.clone.trigger()
|
||||||
|
|
||||||
for what in actions:
|
def populate_menu(m, items, iactions):
|
||||||
|
for what in items:
|
||||||
if what is None:
|
if what is None:
|
||||||
continue
|
m.addSeparator()
|
||||||
elif what == 'Location Manager':
|
elif what in iactions:
|
||||||
for ac in self.location_manager.all_actions:
|
m.addAction(CloneAction(iactions[what].qaction, m))
|
||||||
ac = self.build_menu(ac)
|
|
||||||
|
class MenuBar(QObject):
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_menubar(self):
|
||||||
|
return self.gui.native_menubar
|
||||||
|
|
||||||
|
def __init__(self, location_manager, parent):
|
||||||
|
QObject.__init__(self, parent)
|
||||||
|
self.gui = parent
|
||||||
|
|
||||||
|
self.location_manager = location_manager
|
||||||
|
self.added_actions = []
|
||||||
|
self.last_actions = []
|
||||||
|
|
||||||
|
self.donate_action = QAction(_('Donate'), self)
|
||||||
|
self.donate_menu = QMenu()
|
||||||
|
self.donate_menu.addAction(self.gui.donate_action)
|
||||||
|
self.donate_action.setMenu(self.donate_menu)
|
||||||
|
self.refresh_timer = t = QTimer(self)
|
||||||
|
t.setInterval(200), t.setSingleShot(True), t.timeout.connect(self.refresh_bar)
|
||||||
|
|
||||||
|
def init_bar(self, actions):
|
||||||
|
self.last_actions = actions
|
||||||
|
for ac in self.added_actions:
|
||||||
|
m = ac.menu()
|
||||||
|
if m is not None:
|
||||||
|
m.setVisible(False)
|
||||||
|
|
||||||
|
mb = self.native_menubar
|
||||||
|
for ac in self.added_actions:
|
||||||
|
mb.removeAction(ac)
|
||||||
|
if ac is not self.donate_action:
|
||||||
|
ac.setMenu(None)
|
||||||
|
ac.deleteLater()
|
||||||
|
self.added_actions = []
|
||||||
|
|
||||||
|
for what in actions:
|
||||||
|
if what is None:
|
||||||
|
continue
|
||||||
|
elif what == 'Location Manager':
|
||||||
|
for ac in self.location_manager.available_actions:
|
||||||
|
self.build_menu(ac, ac.isVisible())
|
||||||
|
elif what == 'Donate':
|
||||||
|
mb.addAction(self.donate_action)
|
||||||
|
elif what in self.gui.iactions:
|
||||||
|
action = self.gui.iactions[what]
|
||||||
|
self.build_menu(action.qaction)
|
||||||
|
|
||||||
|
def build_menu(self, ac, visible=True):
|
||||||
|
ans = CloneAction(ac, self.native_menubar, is_top_level=True)
|
||||||
|
ans.setVisible(visible)
|
||||||
|
if ans.menu() is None:
|
||||||
|
m = QMenu()
|
||||||
|
m.addAction(CloneAction(ac, self.native_menubar))
|
||||||
|
ans.setMenu(m)
|
||||||
|
# Qt (as of 5.3.0) does not update global menubar entries
|
||||||
|
# correctly, so we have to rebuild the global menubar.
|
||||||
|
# Without this the Choose Library action shows the text
|
||||||
|
# 'Untitled' and the Location Manager items do not work.
|
||||||
|
ans.text_changed.connect(self.refresh_timer.start)
|
||||||
|
ans.visibility_changed.connect(self.refresh_timer.start)
|
||||||
|
self.native_menubar.addAction(ans)
|
||||||
|
self.added_actions.append(ans)
|
||||||
|
return ans
|
||||||
|
|
||||||
|
def setVisible(self, yes):
|
||||||
|
pass # no-op on OS X since menu bar is always visible
|
||||||
|
|
||||||
|
def update_lm_actions(self):
|
||||||
|
pass # no-op as this is taken care of by init_bar()
|
||||||
|
|
||||||
|
def refresh_bar(self):
|
||||||
|
self.init_bar(self.last_actions)
|
||||||
|
|
||||||
|
else:
|
||||||
|
|
||||||
|
def populate_menu(m, items, iactions):
|
||||||
|
for what in items:
|
||||||
|
if what is None:
|
||||||
|
m.addSeparator()
|
||||||
|
elif what in iactions:
|
||||||
|
m.addAction(iactions[what].qaction)
|
||||||
|
|
||||||
|
class MenuBar(QMenuBar):
|
||||||
|
|
||||||
|
def __init__(self, location_manager, parent):
|
||||||
|
QMenuBar.__init__(self, parent)
|
||||||
|
parent.setMenuBar(self)
|
||||||
|
self.gui = parent
|
||||||
|
|
||||||
|
self.location_manager = location_manager
|
||||||
|
self.added_actions = []
|
||||||
|
|
||||||
|
self.donate_action = QAction(_('Donate'), self)
|
||||||
|
self.donate_menu = QMenu()
|
||||||
|
self.donate_menu.addAction(self.gui.donate_action)
|
||||||
|
self.donate_action.setMenu(self.donate_menu)
|
||||||
|
|
||||||
|
def init_bar(self, actions):
|
||||||
|
for ac in self.added_actions:
|
||||||
|
m = ac.menu()
|
||||||
|
if m is not None:
|
||||||
|
m.setVisible(False)
|
||||||
|
|
||||||
|
self.clear()
|
||||||
|
self.added_actions = []
|
||||||
|
|
||||||
|
for what in actions:
|
||||||
|
if what is None:
|
||||||
|
continue
|
||||||
|
elif what == 'Location Manager':
|
||||||
|
for ac in self.location_manager.all_actions:
|
||||||
|
ac = self.build_menu(ac)
|
||||||
|
self.addAction(ac)
|
||||||
|
self.added_actions.append(ac)
|
||||||
|
ac.setVisible(False)
|
||||||
|
elif what == 'Donate':
|
||||||
|
self.addAction(self.donate_action)
|
||||||
|
elif what in self.gui.iactions:
|
||||||
|
action = self.gui.iactions[what]
|
||||||
|
ac = self.build_menu(action.qaction)
|
||||||
self.addAction(ac)
|
self.addAction(ac)
|
||||||
self.added_actions.append(ac)
|
self.added_actions.append(ac)
|
||||||
ac.setVisible(False)
|
|
||||||
elif what == 'Donate':
|
|
||||||
self.addAction(self.donate_action)
|
|
||||||
elif what in self.gui.iactions:
|
|
||||||
action = self.gui.iactions[what]
|
|
||||||
ac = self.build_menu(action.qaction)
|
|
||||||
self.addAction(ac)
|
|
||||||
self.added_actions.append(ac)
|
|
||||||
|
|
||||||
def build_menu(self, action):
|
def build_menu(self, action):
|
||||||
m = action.menu()
|
m = action.menu()
|
||||||
ac = MenuAction(action, self)
|
ac = MenuAction(action, self)
|
||||||
if m is None:
|
if m is None:
|
||||||
m = QMenu()
|
m = QMenu()
|
||||||
m.addAction(action)
|
m.addAction(action)
|
||||||
ac.setMenu(m)
|
ac.setMenu(m)
|
||||||
return ac
|
return ac
|
||||||
|
|
||||||
|
def update_lm_actions(self):
|
||||||
|
for ac in self.added_actions:
|
||||||
|
clone = getattr(ac, 'clone', None)
|
||||||
|
if clone is not None and clone in self.location_manager.all_actions:
|
||||||
|
ac.setVisible(clone in self.location_manager.available_actions)
|
||||||
|
|
||||||
# }}}
|
# }}}
|
||||||
|
|
||||||
@ -275,7 +418,6 @@ class BarsManager(QObject):
|
|||||||
self.child_bars = tuple(bars[2:])
|
self.child_bars = tuple(bars[2:])
|
||||||
|
|
||||||
self.menu_bar = MenuBar(self.location_manager, self.parent())
|
self.menu_bar = MenuBar(self.location_manager, self.parent())
|
||||||
self.parent().setMenuBar(self.menu_bar)
|
|
||||||
|
|
||||||
self.apply_settings()
|
self.apply_settings()
|
||||||
self.init_bars()
|
self.init_bars()
|
||||||
|
@ -70,16 +70,11 @@ class LibraryViewMixin(object): # {{{
|
|||||||
self.library_view.model().set_highlight_only(config['highlight_search_matches'])
|
self.library_view.model().set_highlight_only(config['highlight_search_matches'])
|
||||||
|
|
||||||
def build_context_menus(self):
|
def build_context_menus(self):
|
||||||
|
from calibre.gui2.bars import populate_menu
|
||||||
lm = QMenu(self)
|
lm = QMenu(self)
|
||||||
def populate_menu(m, items):
|
populate_menu(lm, gprefs['action-layout-context-menu'], self.iactions)
|
||||||
for what in items:
|
|
||||||
if what is None:
|
|
||||||
m.addSeparator()
|
|
||||||
elif what in self.iactions:
|
|
||||||
m.addAction(self.iactions[what].qaction)
|
|
||||||
populate_menu(lm, gprefs['action-layout-context-menu'])
|
|
||||||
dm = QMenu(self)
|
dm = QMenu(self)
|
||||||
populate_menu(dm, gprefs['action-layout-context-menu-device'])
|
populate_menu(dm, gprefs['action-layout-context-menu-device'], self.iactions)
|
||||||
ec = self.iactions['Edit Collections'].qaction
|
ec = self.iactions['Edit Collections'].qaction
|
||||||
self.library_view.set_context_menu(lm, ec)
|
self.library_view.set_context_menu(lm, ec)
|
||||||
for v in (self.memory_view, self.card_a_view, self.card_b_view):
|
for v in (self.memory_view, self.card_a_view, self.card_b_view):
|
||||||
@ -88,7 +83,7 @@ class LibraryViewMixin(object): # {{{
|
|||||||
if self.cover_flow is not None:
|
if self.cover_flow is not None:
|
||||||
cm = QMenu(self.cover_flow)
|
cm = QMenu(self.cover_flow)
|
||||||
populate_menu(cm,
|
populate_menu(cm,
|
||||||
gprefs['action-layout-context-menu-cover-browser'])
|
gprefs['action-layout-context-menu-cover-browser'], self.iactions)
|
||||||
self.cover_flow.set_context_menu(cm)
|
self.cover_flow.set_context_menu(cm)
|
||||||
|
|
||||||
def search_done(self, view, ok):
|
def search_done(self, view, ok):
|
||||||
|
@ -43,10 +43,10 @@ class GarbageCollector(QObject):
|
|||||||
self.threshold = gc.get_threshold()
|
self.threshold = gc.get_threshold()
|
||||||
gc.disable()
|
gc.disable()
|
||||||
self.timer.start(self.INTERVAL)
|
self.timer.start(self.INTERVAL)
|
||||||
#gc.set_debug(gc.DEBUG_SAVEALL)
|
# gc.set_debug(gc.DEBUG_SAVEALL)
|
||||||
|
|
||||||
def check(self):
|
def check(self):
|
||||||
#return self.debug_cycles()
|
# return self.debug_cycles()
|
||||||
l0, l1, l2 = gc.get_count()
|
l0, l1, l2 = gc.get_count()
|
||||||
if self.debug:
|
if self.debug:
|
||||||
print ('gc_check called:', l0, l1, l2)
|
print ('gc_check called:', l0, l1, l2)
|
||||||
@ -97,6 +97,10 @@ class MainWindow(QMainWindow):
|
|||||||
quit_action.setMenuRole(QAction.QuitRole)
|
quit_action.setMenuRole(QAction.QuitRole)
|
||||||
return preferences_action, quit_action
|
return preferences_action, quit_action
|
||||||
|
|
||||||
|
@property
|
||||||
|
def native_menubar(self):
|
||||||
|
return self.___menu_bar
|
||||||
|
|
||||||
def __init__(self, opts, parent=None, disable_automatic_gc=False):
|
def __init__(self, opts, parent=None, disable_automatic_gc=False):
|
||||||
QMainWindow.__init__(self, parent)
|
QMainWindow.__init__(self, parent)
|
||||||
if disable_automatic_gc:
|
if disable_automatic_gc:
|
||||||
|
Loading…
x
Reference in New Issue
Block a user