329 lines
7.9 KiB
Python
329 lines
7.9 KiB
Python
import typing
|
|
|
|
from qtpy import QtCore as QC
|
|
from qtpy import QtWidgets as QW
|
|
from qtpy import QtGui as QG
|
|
|
|
from hydrus.core import HydrusConstants as HC
|
|
from hydrus.core import HydrusData
|
|
from hydrus.core import HydrusGlobals as HG
|
|
from hydrus.core import HydrusProfiling
|
|
from hydrus.core import HydrusText
|
|
|
|
from hydrus.client import ClientGlobals as CG
|
|
|
|
from hydrus.client.gui import QtPorting as QP
|
|
|
|
def AppendMenu( menu, submenu, label ):
|
|
|
|
label = SanitiseLabel( label )
|
|
|
|
submenu.setTitle( label )
|
|
menu_action = menu.addMenu( submenu )
|
|
|
|
return menu_action
|
|
|
|
|
|
def AppendMenuIconItem( menu: QW.QMenu, label: str, description: str, icon: QG.QIcon, callable, *args, **kwargs ):
|
|
|
|
menu_item = QW.QAction( menu )
|
|
|
|
SetMenuTexts( menu_item, label, description )
|
|
|
|
menu_item.setIcon( icon )
|
|
|
|
menu.addAction(menu_item)
|
|
|
|
BindMenuItem( menu_item, callable, *args, **kwargs )
|
|
|
|
return menu_item
|
|
|
|
|
|
def AppendMenuBitmapItem( menu, label, description, bitmap, callable, *args, **kwargs ):
|
|
|
|
return AppendMenuIconItem(menu, label, description, QG.QIcon( bitmap ), callable, *args, **kwargs)
|
|
|
|
|
|
def AppendMenuCheckItem( menu, label, description, initial_value, callable, *args, **kwargs ):
|
|
|
|
menu_item = QW.QAction( menu )
|
|
|
|
SetMenuTexts( menu_item, label, description )
|
|
|
|
menu_item.setCheckable( True )
|
|
menu_item.setChecked( initial_value )
|
|
|
|
menu.addAction( menu_item )
|
|
|
|
BindMenuItem( menu_item, callable, *args, **kwargs )
|
|
|
|
return menu_item
|
|
|
|
def AppendMenuItem( menu, label, description, callable, *args, role: QW.QAction.MenuRole = None, **kwargs ):
|
|
|
|
menu_item = QW.QAction( menu )
|
|
|
|
if HC.PLATFORM_MACOS:
|
|
|
|
menu_item.setMenuRole( role if role is not None else QW.QAction.MenuRole.NoRole )
|
|
|
|
|
|
SetMenuTexts( menu_item, label, description )
|
|
|
|
menu.addAction( menu_item )
|
|
|
|
BindMenuItem( menu_item, callable, *args, **kwargs )
|
|
|
|
return menu_item
|
|
|
|
|
|
def AppendMenuLabel( menu, label, description = '', copy_text = '' ):
|
|
|
|
if description == label:
|
|
|
|
description = ''
|
|
|
|
|
|
if copy_text == '':
|
|
|
|
copy_text = label
|
|
|
|
|
|
if description == '':
|
|
|
|
description = f'copy "{copy_text}" to clipboard'
|
|
|
|
|
|
menu_item = QW.QAction( menu )
|
|
|
|
SetMenuTexts( menu_item, label, description )
|
|
|
|
menu.addAction( menu_item )
|
|
|
|
BindMenuItem( menu_item, CG.client_controller.pub, 'clipboard', 'text', copy_text )
|
|
|
|
return menu_item
|
|
|
|
def AppendMenuOrItem( menu, submenu_name, menu_tuples, sort_tuples = True ):
|
|
|
|
if sort_tuples:
|
|
|
|
try:
|
|
|
|
menu_tuples = sorted( menu_tuples )
|
|
|
|
except:
|
|
|
|
pass
|
|
|
|
|
|
|
|
if len( menu_tuples ) == 1:
|
|
|
|
submenu = menu
|
|
|
|
item_prefix = '{} '.format( submenu_name )
|
|
|
|
else:
|
|
|
|
submenu = GenerateMenu( menu )
|
|
|
|
AppendMenu( menu, submenu, submenu_name )
|
|
|
|
item_prefix = ''
|
|
|
|
|
|
for ( label, description, call ) in menu_tuples:
|
|
|
|
label = '{}{}'.format( item_prefix, label )
|
|
|
|
AppendMenuItem( submenu, label, description, call )
|
|
|
|
|
|
def AppendSeparator( menu ):
|
|
|
|
num_items = len( menu.actions() )
|
|
|
|
if num_items > 0:
|
|
|
|
last_item = menu.actions()[-1]
|
|
|
|
# got this once, who knows what happened, so we test for QAction now
|
|
# 'PySide2.QtGui.QStandardItem' object has no attribute 'isSeparator'
|
|
last_item_is_separator = isinstance( last_item, QW.QAction ) and last_item.isSeparator()
|
|
|
|
if not last_item_is_separator:
|
|
|
|
menu.addSeparator()
|
|
|
|
|
|
|
|
|
|
def BindMenuItem( menu_item, callable, *args, **kwargs ):
|
|
|
|
event_callable = GetEventCallable( callable, *args, **kwargs )
|
|
|
|
menu_item.triggered.connect( event_callable )
|
|
|
|
|
|
def DestroyMenu( menu ):
|
|
|
|
if menu is None:
|
|
|
|
return
|
|
|
|
|
|
if QP.isValid( menu ):
|
|
|
|
menu.deleteLater()
|
|
|
|
|
|
|
|
|
|
class StatusBarRedirectFilter( QC.QObject ):
|
|
|
|
def eventFilter( self, watched, event ):
|
|
|
|
try:
|
|
|
|
if event.type() == QC.QEvent.StatusTip:
|
|
|
|
QW.QApplication.instance().sendEvent( CG.client_controller.gui, event )
|
|
|
|
return True
|
|
|
|
|
|
except Exception as e:
|
|
|
|
HydrusData.ShowException( e )
|
|
|
|
return True
|
|
|
|
|
|
return False
|
|
|
|
|
|
|
|
def GenerateMenu( parent: QW.QWidget ) -> QW.QMenu:
|
|
|
|
menu = QW.QMenu( parent )
|
|
|
|
menu.setToolTipsVisible( True )
|
|
|
|
menu.installEventFilter( StatusBarRedirectFilter( menu ) )
|
|
|
|
return menu
|
|
|
|
|
|
def GetEventCallable( callable, *args, **kwargs ):
|
|
|
|
def event_callable( checked_state ):
|
|
|
|
if HG.profile_mode:
|
|
|
|
summary = 'Profiling menu: ' + repr( callable )
|
|
|
|
HydrusProfiling.Profile( summary, 'callable( *args, **kwargs )', globals(), locals(), min_duration_ms = HG.menu_profile_min_job_time_ms )
|
|
|
|
else:
|
|
|
|
callable( *args, **kwargs )
|
|
|
|
|
|
|
|
return event_callable
|
|
|
|
def SanitiseLabel( label: str ) -> str:
|
|
|
|
if label == '':
|
|
|
|
label = '-invalid label-'
|
|
|
|
|
|
return label.replace( '&', '&&' )
|
|
|
|
|
|
def SetMenuItemLabel( menu_item: QW.QAction, label: str ):
|
|
|
|
label = SanitiseLabel( label )
|
|
|
|
menu_item.setText( label )
|
|
|
|
|
|
def SetMenuTexts( menu_item: QW.QAction, label: str, description: str ):
|
|
|
|
label = SanitiseLabel( label )
|
|
|
|
elided_label = HydrusText.ElideText( label, 128, elide_center = True )
|
|
|
|
menu_item.setText( elided_label )
|
|
|
|
menu_item.setStatusTip( description )
|
|
|
|
if label != elided_label:
|
|
|
|
menu_item.setToolTip( label )
|
|
|
|
elif description != label and description != '':
|
|
|
|
menu_item.setToolTip( description )
|
|
|
|
|
|
menu_item.setWhatsThis( description )
|
|
|
|
|
|
def SetMenuTitle( menu: QW.QMenu, label: str ):
|
|
|
|
label = SanitiseLabel( label )
|
|
|
|
menu.setTitle( label )
|
|
|
|
|
|
def SpamItems( menu: QW.QMenu, labels_descriptions_and_calls: typing.Collection[ typing.Tuple[ str, str, typing.Callable ] ], max_allowed: int ):
|
|
|
|
if len( labels_descriptions_and_calls ) > max_allowed:
|
|
|
|
num_to_show = max_allowed - 1
|
|
|
|
else:
|
|
|
|
num_to_show = max_allowed
|
|
|
|
|
|
for ( label, description, call ) in list( labels_descriptions_and_calls )[:num_to_show]:
|
|
|
|
AppendMenuItem( menu, label, description, call )
|
|
|
|
|
|
if len( labels_descriptions_and_calls ) > num_to_show:
|
|
|
|
# maybe one day this becomes a thing that extends the menu to show them all
|
|
|
|
AppendMenuLabel( menu, '{} more...'.format( len( labels_descriptions_and_calls ) - num_to_show ) )
|
|
|
|
|
|
|
|
def SpamLabels( menu: QW.QMenu, labels_and_copy_texts: typing.Collection[ typing.Tuple[ str, str ] ], max_allowed: int ):
|
|
|
|
if len( labels_and_copy_texts ) > max_allowed:
|
|
|
|
num_to_show = max_allowed - 1
|
|
|
|
else:
|
|
|
|
num_to_show = max_allowed
|
|
|
|
|
|
for ( label, copy_text ) in list( labels_and_copy_texts )[:num_to_show]:
|
|
|
|
AppendMenuLabel( menu, label, copy_text = copy_text )
|
|
|
|
|
|
if len( labels_and_copy_texts ) > num_to_show:
|
|
|
|
# maybe one day this becomes a thing that extends the menu to show them all
|
|
|
|
AppendMenuLabel( menu, '{} more...'.format( len( labels_and_copy_texts ) - num_to_show ) )
|
|
|
|
|