hydrus/hydrus/client/gui/ClientGUIScrolledPanels.py

295 lines
8.7 KiB
Python

from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from qtpy import QtGui as QG
from hydrus.core import HydrusData
from hydrus.core import HydrusTime
from hydrus.client import ClientApplicationCommand as CAC
from hydrus.client import ClientConstants as CC
from hydrus.client.gui import ClientGUIShortcuts
from hydrus.client.gui import ClientGUITopLevelWindows
from hydrus.client.gui import QtPorting as QP
class ResizingEventFilter( QC.QObject ):
def eventFilter( self, watched, event ):
try:
if event.type() == QC.QEvent.Resize:
parent = self.parent()
if isinstance( parent, ResizingScrolledPanel ):
# weird hack fix for a guy who was getting QPaintEvents in here
if not hasattr( event, 'oldSize' ):
return False
old_size = event.oldSize()
size = event.size()
width_larger = size.width() > old_size.width() and size.height() >= old_size.height()
height_larger = size.width() >= old_size.width() and size.height() > old_size.height()
if width_larger or height_larger:
QP.CallAfter( parent.WidgetJustSized, width_larger, height_larger )
except Exception as e:
HydrusData.ShowException( e )
return True
return False
class ResizingScrolledPanel( QW.QScrollArea ):
okSignal = QC.Signal()
def __init__( self, parent ):
QW.QScrollArea.__init__( self, parent )
self.setWidget( QW.QWidget( self ) )
self.setWidgetResizable( True )
self.widget().installEventFilter( ResizingEventFilter( self ) )
self._last_just_sized_cascade_start_time = 0.0
self._number_of_just_sizedes = 0
def _OKParent( self ):
self.okSignal.emit()
def CheckValid( self ):
pass
def CleanBeforeDestroy( self ):
pass
def sizeHint( self ):
if self.widget():
# just as a fun note, QScrollArea does a 12 x 8 character height sizeHint on its own here due to as-yet invalid widget size, wew lad
frame_width = self.frameWidth()
frame_size = QC.QSize( frame_width * 2, frame_width * 2 )
size_hint = self.widget().sizeHint() + frame_size
#visible_size = self.widget().visibleRegion().boundingRect().size()
#size_hint = self.widget().sizeHint() + self.size() - visible_size
available_screen_size = self.screen().availableGeometry().size()
screen_fill_factor = 0.85 # don't let size hint be bigger than this percentage of the available screen width/height
if size_hint.width() > screen_fill_factor * available_screen_size.width():
size_hint.setWidth( int( screen_fill_factor * available_screen_size.width() ) )
if size_hint.height() > screen_fill_factor * available_screen_size.height():
size_hint.setHeight( int( screen_fill_factor * available_screen_size.height() ) )
return size_hint
else:
return QW.QScrollArea.sizeHint( self )
def UserIsOKToOK( self ):
return True
def UserIsOKToCancel( self ):
return True
def WidgetJustSized( self, width_larger, height_larger ):
if width_larger or height_larger:
# ok this is a stupid sizing hack to stop the ever-growing window that has a sizeHint three pixels bigger than its current size causing a resize growth loop
# if we get a bunch of resizes real quick, we cut them off, hopefully breaking the cycle
if not HydrusTime.TimeHasPassedFloat( self._last_just_sized_cascade_start_time + 1.0 ):
self._number_of_just_sizedes += 1
if self._number_of_just_sizedes >= 5:
return
else:
self._last_just_sized_cascade_start_time = HydrusTime.GetNowFloat()
self._number_of_just_sizedes = 1
widget_minimum_size_hint = self.widget().minimumSizeHint()
widget_normal_size_hint = self.widget().sizeHint()
widget_size_hint = QC.QSize( max( widget_minimum_size_hint.width(), widget_normal_size_hint.width() ), max( widget_minimum_size_hint.height(), widget_normal_size_hint.height() ) )
my_size = self.size()
width_increase = 0
height_increase = 0
if width_larger:
width_increase = max( 0, widget_size_hint.width() - my_size.width() )
if height_larger:
height_increase = max( 0, widget_size_hint.height() - my_size.height() )
if width_increase > 0 or height_increase > 0:
window = self.window()
if isinstance( window, ( ClientGUITopLevelWindows.DialogThatResizes, ClientGUITopLevelWindows.FrameThatResizes ) ):
desired_size_delta = QC.QSize( width_increase, height_increase )
ClientGUITopLevelWindows.ExpandTLWIfPossible( window, window._frame_key, desired_size_delta )
class EditPanel( ResizingScrolledPanel ):
def GetValue( self ):
raise NotImplementedError()
def CheckValid( self ):
# raises veto if not valid
self.GetValue()
class EditSingleCtrlPanel( CAC.ApplicationCommandProcessorMixin, EditPanel ):
def __init__( self, parent, ok_on_these_commands = None, message = None ):
EditPanel.__init__( self, parent )
CAC.ApplicationCommandProcessorMixin.__init__( self )
self._control = None
if ok_on_these_commands is None:
ok_on_these_commands = set()
self._ok_on_these_commands = set( ok_on_these_commands )
#
self._vbox = QP.VBoxLayout( margin = 0 )
if message is not None:
from hydrus.client.gui.widgets import ClientGUICommon
st = ClientGUICommon.BetterStaticText( self, label = message )
st.setWordWrap( True )
QP.AddToLayout( self._vbox, st, CC.FLAGS_EXPAND_PERPENDICULAR )
self.widget().setLayout( self._vbox )
self._my_shortcuts_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media' ] )
def GetValue( self ):
if hasattr( self._control, 'GetValue' ):
return self._control.GetValue()
elif hasattr( self._control, 'toPlainText' ):
return self._control.toPlainText()
return self._control.value()
def ProcessApplicationCommand( self, command: CAC.ApplicationCommand ):
command_processed = True
if command.IsSimpleCommand():
action = command.GetSimpleAction()
if action in self._ok_on_these_commands:
self._OKParent()
else:
command_processed = False
else:
command_processed = False
return command_processed
def SetControl( self, control ):
self._control = control
QP.AddToLayout( self._vbox, control, CC.FLAGS_EXPAND_BOTH_WAYS )
class ManagePanel( ResizingScrolledPanel ):
def CommitChanges( self ):
raise NotImplementedError()
class ReviewPanel( ResizingScrolledPanel ):
pass