Version 383

This commit is contained in:
Hydrus Network Developer 2020-02-05 16:55:21 -06:00
parent 0ee29e4897
commit 00dbc2daca
20 changed files with 891 additions and 234 deletions

View File

@ -8,6 +8,30 @@
<div class="content">
<h3>changelog</h3>
<ul>
<li><h3>version 383</h3></li>
<ul>
<li>mpv:</li>
<li>updated the prototype volume/mute controls on the top media viewer hover window to be a proper 'speaker' icon button for mute with a volume slider that pops up or down on mouse-over</li>
<li>the new volume control is on the hover window and any media that has audio</li>
<li>the right-click menu of the preview viewer and media viewer now have volume submenus to set mute/volume</li>
<li>the client now has multiple volumes and mutes:</li>
<li>for mute, there is a global mute which overrides everything, and the preview and media viewers have their own mutes that just apply there.</li>
<li>under options->audio, you can choose whether preview windows have their own separate volume value, default is yes they do</li>
<li>there is a new shortcut set called 'global', which applies on the main gui and the media viewer both, and which currently has actions to alter global mute. by default, ctrl+g flips global mute</li>
<li>after reports of unusual rendering bugs for some users, the default mpv.conf is now more barebones. more work will happen here</li>
<li>.</li>
<li>linux:</li>
<li>the linux release is now built on Ubuntu 18.04 (was 16.04). unfortunately, my build packager bundled in a variety of surplus libraries, so the archive has bloated somewhat--I have removed some that I am confident are not needed, but I may have made a mistake, and there are likely more that can be taken away</li>
<li>the linux release now comes with mpv support</li>
<li>please let me know if you have any errors running this build or loading mpv. early tests seem good though!</li>
<li>.</li>
<li>the rest:</li>
<li>the launch/exit splash screen now uses a cleaner Qt-compatible layout system. It resizes and obeys stylesheets better, colouring text and background according to current style</li>
<li>removed the 'has duration' text label option from 'audio and duration' options panel as it is no longer used, and renamed the panel back to just 'audio'</li>
<li>the string transformation edit panel's individual transformation edit panel now shows that transformation step's example string and the transformed string, which is updated by button. this edit panel will get some more love soon, including dynamic hide/show of applicable controls and live updates of the example transformation as you type</li>
<li>misc ui layout improvements</li>
<li>misc ui improvements</li>
</ul>
<li><h3>version 382</h3></li>
<ul>
<li>mpv:</li>

View File

@ -401,10 +401,11 @@ shortcut_mouse_string_lookup[ SHORTCUT_MOUSE_SCROLL_DOWN ] = 'scroll down'
shortcut_mouse_string_lookup[ SHORTCUT_MOUSE_SCROLL_LEFT ] = 'scroll left'
shortcut_mouse_string_lookup[ SHORTCUT_MOUSE_SCROLL_RIGHT ] = 'scroll right'
SHORTCUTS_RESERVED_NAMES = [ 'archive_delete_filter', 'duplicate_filter', 'media', 'main_gui', 'media_viewer_browser', 'media_viewer' ]
SHORTCUTS_RESERVED_NAMES = [ 'global', 'archive_delete_filter', 'duplicate_filter', 'media', 'main_gui', 'media_viewer_browser', 'media_viewer' ]
shortcut_names_to_descriptions = {}
shortcut_names_to_descriptions[ 'global' ] = 'Actions for the whole program. Should work in the main gui or a media viewer.'
shortcut_names_to_descriptions[ 'archive_delete_filter' ] = 'Navigation actions for the media viewer during an archive/delete filter. Mouse shortcuts should work.'
shortcut_names_to_descriptions[ 'duplicate_filter' ] = 'Navigation actions for the media viewer during a duplicate filter. Mouse shortcuts should work.'
shortcut_names_to_descriptions[ 'media' ] = 'Actions to alter metadata for media in the media viewer or the thumbnail grid.'
@ -414,6 +415,7 @@ shortcut_names_to_descriptions[ 'media_viewer' ] = 'Zoom and pan actions for any
# shortcut commands
SHORTCUTS_GLOBAL_ACTIONS = [ 'global_audio_mute', 'global_audio_unmute', 'global_audio_mute_flip' ]
SHORTCUTS_MEDIA_ACTIONS = [ 'manage_file_tags', 'manage_file_ratings', 'manage_file_urls', 'manage_file_notes', 'archive_file', 'inbox_file', 'delete_file', 'export_files', 'export_files_quick_auto_export', 'remove_file_from_view', 'open_file_in_external_program', 'open_selection_in_new_page', 'launch_the_archive_delete_filter', 'copy_bmp', 'copy_file', 'copy_path', 'copy_sha256_hash', 'get_similar_to_exact', 'get_similar_to_very_similar', 'get_similar_to_similar', 'get_similar_to_speculative', 'duplicate_media_set_alternate', 'duplicate_media_set_alternate_collections', 'duplicate_media_set_custom', 'duplicate_media_set_focused_better', 'duplicate_media_set_focused_king', 'duplicate_media_set_same_quality', 'open_known_url' ]
SHORTCUTS_MEDIA_VIEWER_ACTIONS = [ 'pause_media', 'pause_play_media', 'move_animation_to_previous_frame', 'move_animation_to_next_frame', 'switch_between_fullscreen_borderless_and_regular_framed_window', 'pan_up', 'pan_down', 'pan_left', 'pan_right', 'pan_top_edge', 'pan_bottom_edge', 'pan_left_edge', 'pan_right_edge', 'pan_vertical_center', 'pan_horizontal_center', 'zoom_in', 'zoom_out', 'switch_between_100_percent_and_canvas_zoom', 'flip_darkmode' ]
SHORTCUTS_MEDIA_VIEWER_BROWSER_ACTIONS = [ 'view_next', 'view_first', 'view_last', 'view_previous', 'pause_play_slideshow' ]
@ -423,6 +425,7 @@ SHORTCUTS_ARCHIVE_DELETE_FILTER_ACTIONS = [ 'archive_delete_filter_keep', 'archi
simple_shortcut_name_to_action_lookup = {}
simple_shortcut_name_to_action_lookup[ 'global' ] = SHORTCUTS_GLOBAL_ACTIONS
simple_shortcut_name_to_action_lookup[ 'media' ] = SHORTCUTS_MEDIA_ACTIONS
simple_shortcut_name_to_action_lookup[ 'media_viewer' ] = SHORTCUTS_MEDIA_VIEWER_ACTIONS
simple_shortcut_name_to_action_lookup[ 'media_viewer_browser' ] = SHORTCUTS_MEDIA_VIEWER_BROWSER_ACTIONS
@ -530,89 +533,91 @@ class GlobalPixmaps( object ):
# These probably *could* be created even before QApplication is constructed, but it can't hurt to wait until that's done.
GlobalPixmaps.bold = QG.QPixmap( os.path.join( HC.STATIC_DIR,'text_bold.png' ) )
GlobalPixmaps.italic = QG.QPixmap( os.path.join( HC.STATIC_DIR,'text_italic.png' ) )
GlobalPixmaps.underline = QG.QPixmap( os.path.join( HC.STATIC_DIR,'text_underline.png' ) )
GlobalPixmaps.bold = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'text_bold.png' ) )
GlobalPixmaps.italic = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'text_italic.png' ) )
GlobalPixmaps.underline = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'text_underline.png' ) )
GlobalPixmaps.align_left = QG.QPixmap( os.path.join( HC.STATIC_DIR,'text_align_left.png' ) )
GlobalPixmaps.align_center = QG.QPixmap( os.path.join( HC.STATIC_DIR,'text_align_center.png' ) )
GlobalPixmaps.align_right = QG.QPixmap( os.path.join( HC.STATIC_DIR,'text_align_right.png' ) )
GlobalPixmaps.align_justify = QG.QPixmap( os.path.join( HC.STATIC_DIR,'text_align_justify.png' ) )
GlobalPixmaps.align_left = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'text_align_left.png' ) )
GlobalPixmaps.align_center = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'text_align_center.png' ) )
GlobalPixmaps.align_right = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'text_align_right.png' ) )
GlobalPixmaps.align_justify = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'text_align_justify.png' ) )
GlobalPixmaps.indent_less = QG.QPixmap( os.path.join( HC.STATIC_DIR,'text_indent_remove.png' ) )
GlobalPixmaps.indent_more = QG.QPixmap( os.path.join( HC.STATIC_DIR,'text_indent.png' ) )
GlobalPixmaps.indent_less = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'text_indent_remove.png' ) )
GlobalPixmaps.indent_more = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'text_indent.png' ) )
GlobalPixmaps.font = QG.QPixmap( os.path.join( HC.STATIC_DIR,'font.png' ) )
GlobalPixmaps.colour = QG.QPixmap( os.path.join( HC.STATIC_DIR,'color_swatch.png' ) )
GlobalPixmaps.font = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'font.png' ) )
GlobalPixmaps.colour = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'color_swatch.png' ) )
GlobalPixmaps.link = QG.QPixmap( os.path.join( HC.STATIC_DIR,'link.png' ) )
GlobalPixmaps.link_break = QG.QPixmap( os.path.join( HC.STATIC_DIR,'link_break.png' ) )
GlobalPixmaps.link = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'link.png' ) )
GlobalPixmaps.link_break = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'link_break.png' ) )
GlobalPixmaps.drag = QG.QPixmap( os.path.join( HC.STATIC_DIR,'drag.png' ) )
GlobalPixmaps.drag = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'drag.png' ) )
GlobalPixmaps.transparent = QG.QPixmap( os.path.join( HC.STATIC_DIR,'transparent.png' ) )
GlobalPixmaps.downloading = QG.QPixmap( os.path.join( HC.STATIC_DIR,'downloading.png' ) )
GlobalPixmaps.file_repository = QG.QPixmap( os.path.join( HC.STATIC_DIR,'file_repository_small.png' ) )
GlobalPixmaps.file_repository_pending = QG.QPixmap( os.path.join( HC.STATIC_DIR,'file_repository_pending_small.png' ) )
GlobalPixmaps.file_repository_petitioned = QG.QPixmap( os.path.join( HC.STATIC_DIR,'file_repository_petitioned_small.png' ) )
GlobalPixmaps.ipfs = QG.QPixmap( os.path.join( HC.STATIC_DIR,'ipfs_small.png' ) )
GlobalPixmaps.ipfs_pending = QG.QPixmap( os.path.join( HC.STATIC_DIR,'ipfs_pending_small.png' ) )
GlobalPixmaps.ipfs_petitioned = QG.QPixmap( os.path.join( HC.STATIC_DIR,'ipfs_petitioned_small.png' ) )
GlobalPixmaps.transparent = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'transparent.png' ) )
GlobalPixmaps.downloading = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'downloading.png' ) )
GlobalPixmaps.file_repository = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'file_repository_small.png' ) )
GlobalPixmaps.file_repository_pending = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'file_repository_pending_small.png' ) )
GlobalPixmaps.file_repository_petitioned = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'file_repository_petitioned_small.png' ) )
GlobalPixmaps.ipfs = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'ipfs_small.png' ) )
GlobalPixmaps.ipfs_pending = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'ipfs_pending_small.png' ) )
GlobalPixmaps.ipfs_petitioned = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'ipfs_petitioned_small.png' ) )
GlobalPixmaps.collection = QG.QPixmap( os.path.join( HC.STATIC_DIR,'collection.png' ) )
GlobalPixmaps.inbox = QG.QPixmap( os.path.join( HC.STATIC_DIR,'inbox.png' ) )
GlobalPixmaps.trash = QG.QPixmap( os.path.join( HC.STATIC_DIR,'trash.png' ) )
GlobalPixmaps.collection = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'collection.png' ) )
GlobalPixmaps.inbox = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'inbox.png' ) )
GlobalPixmaps.trash = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'trash.png' ) )
GlobalPixmaps.refresh = QG.QPixmap( os.path.join( HC.STATIC_DIR,'refresh.png' ) )
GlobalPixmaps.archive = QG.QPixmap( os.path.join( HC.STATIC_DIR,'archive.png' ) )
GlobalPixmaps.to_inbox = QG.QPixmap( os.path.join( HC.STATIC_DIR,'to_inbox.png' ) )
GlobalPixmaps.delete = QG.QPixmap( os.path.join( HC.STATIC_DIR,'trash.png' ) )
GlobalPixmaps.trash_delete = QG.QPixmap( os.path.join( HC.STATIC_DIR,'delete.png' ) )
GlobalPixmaps.undelete = QG.QPixmap( os.path.join( HC.STATIC_DIR,'undelete.png' ) )
GlobalPixmaps.zoom_in = QG.QPixmap( os.path.join( HC.STATIC_DIR,'zoom_in.png' ) )
GlobalPixmaps.zoom_out = QG.QPixmap( os.path.join( HC.STATIC_DIR,'zoom_out.png' ) )
GlobalPixmaps.zoom_switch = QG.QPixmap( os.path.join( HC.STATIC_DIR,'zoom_switch.png' ) )
GlobalPixmaps.fullscreen_switch = QG.QPixmap( os.path.join( HC.STATIC_DIR,'fullscreen_switch.png' ) )
GlobalPixmaps.open_externally = QG.QPixmap( os.path.join( HC.STATIC_DIR,'open_externally.png' ) )
GlobalPixmaps.refresh = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'refresh.png' ) )
GlobalPixmaps.archive = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'archive.png' ) )
GlobalPixmaps.to_inbox = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'to_inbox.png' ) )
GlobalPixmaps.delete = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'trash.png' ) )
GlobalPixmaps.trash_delete = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'delete.png' ) )
GlobalPixmaps.undelete = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'undelete.png' ) )
GlobalPixmaps.zoom_in = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'zoom_in.png' ) )
GlobalPixmaps.zoom_out = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'zoom_out.png' ) )
GlobalPixmaps.zoom_switch = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'zoom_switch.png' ) )
GlobalPixmaps.fullscreen_switch = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'fullscreen_switch.png' ) )
GlobalPixmaps.open_externally = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'open_externally.png' ) )
GlobalPixmaps.dump_ok = QG.QPixmap( os.path.join( HC.STATIC_DIR,'dump_ok.png' ) )
GlobalPixmaps.dump_recoverable = QG.QPixmap( os.path.join( HC.STATIC_DIR,'dump_recoverable.png' ) )
GlobalPixmaps.dump_fail = QG.QPixmap( os.path.join( HC.STATIC_DIR,'dump_fail.png' ) )
GlobalPixmaps.dump_ok = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'dump_ok.png' ) )
GlobalPixmaps.dump_recoverable = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'dump_recoverable.png' ) )
GlobalPixmaps.dump_fail = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'dump_fail.png' ) )
GlobalPixmaps.cog = QG.QPixmap( os.path.join( HC.STATIC_DIR,'cog.png' ) )
GlobalPixmaps.family = QG.QPixmap( os.path.join( HC.STATIC_DIR,'family.png' ) )
GlobalPixmaps.keyboard = QG.QPixmap( os.path.join( HC.STATIC_DIR,'keyboard.png' ) )
GlobalPixmaps.help = QG.QPixmap( os.path.join( HC.STATIC_DIR,'help.png' ) )
GlobalPixmaps.cog = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'cog.png' ) )
GlobalPixmaps.family = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'family.png' ) )
GlobalPixmaps.keyboard = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'keyboard.png' ) )
GlobalPixmaps.help = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'help.png' ) )
GlobalPixmaps.check = QG.QPixmap( os.path.join( HC.STATIC_DIR,'check.png' ) )
GlobalPixmaps.pause = QG.QPixmap( os.path.join( HC.STATIC_DIR,'pause.png' ) )
GlobalPixmaps.play = QG.QPixmap( os.path.join( HC.STATIC_DIR,'play.png' ) )
GlobalPixmaps.stop = QG.QPixmap( os.path.join( HC.STATIC_DIR,'stop.png' ) )
GlobalPixmaps.sound = QG.QPixmap( os.path.join( HC.STATIC_DIR,'sound.png' ) )
GlobalPixmaps.check = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'check.png' ) )
GlobalPixmaps.pause = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'pause.png' ) )
GlobalPixmaps.play = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'play.png' ) )
GlobalPixmaps.stop = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'stop.png' ) )
GlobalPixmaps.file_pause = QG.QPixmap( os.path.join( HC.STATIC_DIR,'file_pause.png' ) )
GlobalPixmaps.file_play = QG.QPixmap( os.path.join( HC.STATIC_DIR,'file_play.png' ) )
GlobalPixmaps.gallery_pause = QG.QPixmap( os.path.join( HC.STATIC_DIR,'gallery_pause.png' ) )
GlobalPixmaps.gallery_play = QG.QPixmap( os.path.join( HC.STATIC_DIR,'gallery_play.png' ) )
GlobalPixmaps.sound = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'sound.png' ) )
GlobalPixmaps.mute = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'mute.png' ) )
GlobalPixmaps.highlight = QG.QPixmap( os.path.join( HC.STATIC_DIR,'highlight.png' ) )
GlobalPixmaps.clear_highlight = QG.QPixmap( os.path.join( HC.STATIC_DIR,'clear_highlight.png' ) )
GlobalPixmaps.file_pause = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'file_pause.png' ) )
GlobalPixmaps.file_play = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'file_play.png' ) )
GlobalPixmaps.gallery_pause = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'gallery_pause.png' ) )
GlobalPixmaps.gallery_play = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'gallery_play.png' ) )
GlobalPixmaps.listctrl = QG.QPixmap( os.path.join( HC.STATIC_DIR,'listctrl.png' ) )
GlobalPixmaps.highlight = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'highlight.png' ) )
GlobalPixmaps.clear_highlight = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'clear_highlight.png' ) )
GlobalPixmaps.copy = QG.QPixmap( os.path.join( HC.STATIC_DIR,'copy.png' ) )
GlobalPixmaps.paste = QG.QPixmap( os.path.join( HC.STATIC_DIR,'paste.png' ) )
GlobalPixmaps.listctrl = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'listctrl.png' ) )
GlobalPixmaps.eight_kun = QG.QPixmap( os.path.join( HC.STATIC_DIR,'8kun.png' ) )
GlobalPixmaps.twitter = QG.QPixmap( os.path.join( HC.STATIC_DIR,'twitter.png' ) )
GlobalPixmaps.tumblr = QG.QPixmap( os.path.join( HC.STATIC_DIR,'tumblr.png' ) )
GlobalPixmaps.discord = QG.QPixmap( os.path.join( HC.STATIC_DIR,'discord.png' ) )
GlobalPixmaps.patreon = QG.QPixmap( os.path.join( HC.STATIC_DIR,'patreon.png' ) )
GlobalPixmaps.copy = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'copy.png' ) )
GlobalPixmaps.paste = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'paste.png' ) )
GlobalPixmaps.first = QG.QPixmap( os.path.join( HC.STATIC_DIR,'first.png' ) )
GlobalPixmaps.previous = QG.QPixmap( os.path.join( HC.STATIC_DIR,'previous.png' ) )
GlobalPixmaps.next_bmp = QG.QPixmap( os.path.join( HC.STATIC_DIR,'next.png' ) )
GlobalPixmaps.last = QG.QPixmap( os.path.join( HC.STATIC_DIR,'last.png' ) )
GlobalPixmaps.eight_kun = QG.QPixmap( os.path.join( HC.STATIC_DIR, '8kun.png' ) )
GlobalPixmaps.twitter = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'twitter.png' ) )
GlobalPixmaps.tumblr = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'tumblr.png' ) )
GlobalPixmaps.discord = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'discord.png' ) )
GlobalPixmaps.patreon = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'patreon.png' ) )
GlobalPixmaps.first = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'first.png' ) )
GlobalPixmaps.previous = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'previous.png' ) )
GlobalPixmaps.next_bmp = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'next.png' ) )
GlobalPixmaps.last = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'last.png' ) )
DEFAULT_LOCAL_TAG_SERVICE_KEY = b'local tags'

View File

@ -13450,6 +13450,24 @@ class DB( HydrusDB.HydrusDB ):
if version == 382:
existing_shortcut_names = self._GetJSONDumpNames( HydrusSerialisable.SERIALISABLE_TYPE_SHORTCUT_SET )
if 'global' not in existing_shortcut_names:
list_of_shortcuts = ClientDefaults.GetDefaultShortcuts()
for shortcuts in list_of_shortcuts:
if shortcuts.GetName() == 'global':
self._SetJSONDump( shortcuts )
self._controller.pub( 'splash_set_title_text', 'updated db to v' + str( version + 1 ) )
self._c.execute( 'UPDATE version SET version = ?;', ( version + 1, ) )

View File

@ -312,6 +312,12 @@ def GetDefaultShortcuts():
shortcuts = []
global_shortcuts = ClientGUIShortcuts.ShortcutSet( 'global' )
global_shortcuts.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_KEYBOARD_CHARACTER, ord( 'G' ), [ CC.SHORTCUT_MODIFIER_CTRL ] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'global_audio_mute_flip' ) )
shortcuts.append( global_shortcuts )
archive_delete_filter = ClientGUIShortcuts.ShortcutSet( 'archive_delete_filter' )
archive_delete_filter.SetCommand( ClientGUIShortcuts.Shortcut( CC.SHORTCUT_TYPE_MOUSE, CC.SHORTCUT_MOUSE_LEFT, [] ), ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'archive_delete_filter_keep' ) )

View File

@ -16,6 +16,7 @@ from . import ClientGUIFunctions
from . import ClientGUIImport
from . import ClientGUILogin
from . import ClientGUIManagement
from . import ClientGUIMediaControls
from . import ClientGUIMenus
from . import ClientGUIMPV
from . import ClientGUIPages
@ -480,7 +481,7 @@ class FrameGUI( ClientGUITopLevelWindows.MainFrameThatResizes ):
self._animation_update_windows = set()
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'main_gui' ] )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'global', 'main_gui' ] )
self._controller.CallLaterQtSafe( self, 0.5, self._InitialiseSession ) # do this in callafter as some pages want to talk to controller.gui, which doesn't exist yet!
@ -5251,6 +5252,18 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
self.FlipDarkmode()
elif action == 'global_audio_mute':
ClientGUIMediaControls.SetMute( ClientGUIMediaControls.AUDIO_GLOBAL, True )
elif action == 'global_audio_unmute':
ClientGUIMediaControls.SetMute( ClientGUIMediaControls.AUDIO_GLOBAL, False )
elif action == 'global_audio_mute_flip':
ClientGUIMediaControls.FlipMute( ClientGUIMediaControls.AUDIO_GLOBAL )
elif action == 'show_hide_splitters':
self._ShowHideSplitters()
@ -5852,19 +5865,21 @@ The password is cleartext here but obscured in the entry dialog. Enter a blank p
class FrameSplashPanel( QW.QWidget ):
WIDTH = 480
HEIGHT = 280
def __init__( self, parent, controller ):
QW.QWidget.__init__( self, parent )
self.setForegroundRole( QG.QPalette.Text )
self._controller = controller
self._my_status = FrameSplashStatus( self._controller, self )
QP.SetClientSize( self, (self.WIDTH,self.HEIGHT) )
QP.SetMinClientSize( self, (self.WIDTH,self.HEIGHT) )
width = ClientGUIFunctions.ConvertTextToPixelWidth( self, 64 )
self.setMinimumWidth( width )
self.setMaximumWidth( width * 2 )
self._drag_last_pos = None
self._initial_position = self.parentWidget().pos()
@ -5872,63 +5887,41 @@ class FrameSplashPanel( QW.QWidget ):
# this is 124 x 166
self._hydrus_pixmap = QG.QPixmap( os.path.join( HC.STATIC_DIR, 'hydrus_splash.png' ) )
self._image_label = QW.QLabel( self )
self._image_label.setPixmap( self._hydrus_pixmap )
self._image_label.setAlignment( QC.Qt.AlignCenter )
QP.SetBackgroundColour( self._image_label, ( 255, 255, 255 ) )
self._title_label = ClientGUICommon.BetterStaticText( self, label = ' ' )
self._status_label = ClientGUICommon.BetterStaticText( self, label = ' ' )
self._status_sub_label = ClientGUICommon.BetterStaticText( self, label = ' ' )
self._title_label.setAlignment( QC.Qt.AlignCenter )
self._status_label.setAlignment( QC.Qt.AlignCenter )
self._status_sub_label.setAlignment( QC.Qt.AlignCenter )
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, self._image_label, CC.FLAGS_EXPAND_BOTH_WAYS )
QP.AddToLayout( vbox, self._title_label, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._status_label, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, self._status_sub_label, CC.FLAGS_EXPAND_PERPENDICULAR )
margin = ClientGUIFunctions.ConvertTextToPixelWidth( self, 3 )
self._image_label.setMargin( margin )
self.setLayout( vbox )
self._widget_event_filter = QP.WidgetEventFilter( self )
self._widget_event_filter.EVT_MOTION( self.EventDrag )
self._widget_event_filter.EVT_LEFT_DOWN( self.EventDragBegin )
self._widget_event_filter.EVT_LEFT_UP( self.EventDragEnd )
def _Redraw( self, painter ):
( title_text, status_text, status_subtext ) = self._my_status.GetTexts()
painter.setBackground( QG.QBrush( QP.GetSystemColour( QG.QPalette.Base ) ) )
painter.eraseRect( painter.viewport() )
#
x = ( self.WIDTH - 124 ) // 2
y = 15
painter.drawPixmap( x, y, self._hydrus_pixmap )
painter.setFont( QW.QApplication.font() )
y += 166 + 15
#
( width, height ) = painter.fontMetrics().size( QC.Qt.TextSingleLine, title_text ).toTuple()
text_gap = ( self.HEIGHT - y - height * 3 ) // 4
x = ( self.WIDTH - width ) // 2
y += text_gap
QP.DrawText( painter, x, y, title_text )
#
y += height + text_gap
( width, height ) = painter.fontMetrics().size( QC.Qt.TextSingleLine, status_text ).toTuple()
x = ( self.WIDTH - width ) // 2
QP.DrawText( painter, x, y, status_text )
#
y += height + text_gap
( width, height ) = painter.fontMetrics().size( QC.Qt.TextSingleLine, status_subtext ).toTuple()
x = ( self.WIDTH - width ) // 2
QP.DrawText( painter, x, y, status_subtext )
def EventDrag( self, event ):
if event.type() == QC.QEvent.MouseMove and ( event.buttons() & QC.Qt.LeftButton ) and self._drag_last_pos is not None:
@ -5957,18 +5950,15 @@ class FrameSplashPanel( QW.QWidget ):
self._drag_last_pos = None
return True # was: event.ignore()
def paintEvent( self, event ):
painter = QG.QPainter( self )
self._Redraw( painter )
def SetDirty( self ):
self.update()
( title_text, status_text, status_subtext ) = self._my_status.GetTexts()
self._title_label.setText( title_text )
self._status_label.setText( status_text )
self._status_sub_label.setText( status_subtext )
# We have this to be an off-Qt-thread-happy container for this info, as the framesplash has to deal with messages in the fuzzy time of shutdown

View File

@ -13,6 +13,7 @@ from . import ClientGUIDialogsQuick
from . import ClientGUIFunctions
from . import ClientGUIHoverFrames
from . import ClientGUIMedia
from . import ClientGUIMediaControls
from . import ClientGUIMenus
from . import ClientGUIMPV
from . import ClientGUIScrolledPanels
@ -42,10 +43,101 @@ from qtpy import QtWidgets as QW
from qtpy import QtGui as QG
from . import QtPorting as QP
OPEN_EXTERNALLY_BUTTON_SIZE = ( 200, 45 )
def AddAudioVolumeMenu( menu, canvas_type ):
mute_volume_type = None
volume_volume_type = ClientGUIMediaControls.AUDIO_GLOBAL
if canvas_type == ClientGUICommon.CANVAS_MEDIA_VIEWER:
mute_volume_type = ClientGUIMediaControls.AUDIO_MEDIA_VIEWER
if HG.client_controller.new_options.GetBoolean( 'media_viewer_uses_its_own_audio_volume' ):
volume_volume_type = ClientGUIMediaControls.AUDIO_MEDIA_VIEWER
elif canvas_type == ClientGUICommon.CANVAS_PREVIEW:
mute_volume_type = ClientGUIMediaControls.AUDIO_PREVIEW
if HG.client_controller.new_options.GetBoolean( 'preview_uses_its_own_audio_volume' ):
volume_volume_type = ClientGUIMediaControls.AUDIO_PREVIEW
volume_menu = QW.QMenu( menu )
( global_mute_option_name, global_volume_option_name ) = ClientGUIMediaControls.volume_types_to_option_names[ ClientGUIMediaControls.AUDIO_GLOBAL ]
if HG.client_controller.new_options.GetBoolean( global_mute_option_name ):
label = 'unmute global'
else:
label = 'mute global'
ClientGUIMenus.AppendMenuItem( volume_menu, label, 'Mute/unmute audio.', ClientGUIMediaControls.FlipMute, ClientGUIMediaControls.AUDIO_GLOBAL )
#
if mute_volume_type is not None:
ClientGUIMenus.AppendSeparator( volume_menu )
( mute_option_name, volume_option_name ) = ClientGUIMediaControls.volume_types_to_option_names[ mute_volume_type ]
if HG.client_controller.new_options.GetBoolean( mute_option_name ):
label = 'unmute {}'.format( ClientGUIMediaControls.volume_types_str_lookup[ mute_volume_type ] )
else:
label = 'mute {}'.format( ClientGUIMediaControls.volume_types_str_lookup[ mute_volume_type ] )
ClientGUIMenus.AppendMenuItem( volume_menu, label, 'Mute/unmute audio.', ClientGUIMediaControls.FlipMute, mute_volume_type )
#
ClientGUIMenus.AppendSeparator( volume_menu )
( mute_option_name, volume_option_name ) = ClientGUIMediaControls.volume_types_to_option_names[ volume_volume_type ]
# 0-100 inclusive
volumes = list( range( 0, 110, 10 ) )
current_volume = HG.client_controller.new_options.GetInteger( volume_option_name )
if current_volume not in volumes:
volumes.append( current_volume )
volumes.sort()
for volume in volumes:
label = 'volume: {}'.format( volume )
if volume == current_volume:
ClientGUIMenus.AppendMenuCheckItem( volume_menu, label, 'Set the volume.', True, ClientGUIMediaControls.ChangeVolume, volume_volume_type, volume )
else:
ClientGUIMenus.AppendMenuItem( volume_menu, label, 'Set the volume.', ClientGUIMediaControls.ChangeVolume, volume_volume_type, volume )
ClientGUIMenus.AppendMenu( menu, volume_menu, 'volume' )
def CalculateCanvasMediaSize( media, canvas_size, show_action ):
( canvas_width, canvas_height ) = canvas_size.toTuple()
@ -982,7 +1074,7 @@ class CanvasFrame( ClientGUITopLevelWindows.FrameThatResizes ):
self._canvas_window = None
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media_viewer' ] )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'global', 'media_viewer' ] )
HG.client_controller.gui.RegisterCanvasFrameReference( self )
@ -1050,6 +1142,18 @@ class CanvasFrame( ClientGUITopLevelWindows.FrameThatResizes ):
HG.client_controller.gui.FlipDarkmode()
elif action == 'global_audio_mute':
ClientGUIMediaControls.SetMute( ClientGUIMediaControls.AUDIO_GLOBAL, True )
elif action == 'global_audio_unmute':
ClientGUIMediaControls.SetMute( ClientGUIMediaControls.AUDIO_GLOBAL, False )
elif action == 'global_audio_mute_flip':
ClientGUIMediaControls.FlipMute( ClientGUIMediaControls.AUDIO_GLOBAL )
else:
command_processed = False
@ -1125,7 +1229,17 @@ class Canvas( QW.QWidget ):
self._service_keys_to_services = {}
self._current_media = None
self._media_container = MediaContainer( self )
if self.PREVIEW_WINDOW:
self._canvas_type = ClientGUICommon.CANVAS_PREVIEW
else:
self._canvas_type = ClientGUICommon.CANVAS_MEDIA_VIEWER
self._media_container = MediaContainer( self, self._canvas_type )
self._current_zoom = 1.0
self._canvas_zoom = 1.0
@ -2493,6 +2607,8 @@ class CanvasPanel( Canvas ):
return
menu = QW.QMenu()
if self._current_media is not None:
new_options = HG.client_controller.new_options
@ -2507,8 +2623,6 @@ class CanvasPanel( Canvas ):
i_can_post_ratings = len( local_ratings_services ) > 0
menu = QW.QMenu()
#
info_lines = self._current_media.GetPrettyInfoLines()
@ -2526,6 +2640,13 @@ class CanvasPanel( Canvas ):
ClientGUIMenus.AppendMenu( menu, info_menu, top_line )
ClientGUIMenus.AppendSeparator( menu )
AddAudioVolumeMenu( menu, self._canvas_type )
if self._current_media is not None:
#
ClientGUIMenus.AppendSeparator( menu )
@ -2612,8 +2733,8 @@ class CanvasPanel( Canvas ):
ClientGUIMenus.AppendMenu( menu, share_menu, 'share' )
HG.client_controller.PopupMenu( self, menu )
HG.client_controller.PopupMenu( self, menu )
def MediaFocusWentToExternalProgram( self, page_key ):
@ -4932,6 +5053,8 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
ClientGUIMenus.AppendMenu( menu, zoom_menu, 'current zoom: {}'.format( ClientData.ConvertZoomToPercentage( self._current_zoom ) ) )
AddAudioVolumeMenu( menu, self._canvas_type )
if self.parentWidget().isFullScreen():
ClientGUIMenus.AppendMenuItem( menu, 'exit fullscreen', 'Make this media viewer a regular window with borders.', self.parentWidget().FullscreenSwitch )
@ -5144,10 +5267,12 @@ class CanvasMediaListBrowser( CanvasMediaListNavigable ):
class MediaContainer( QW.QWidget ):
def __init__( self, parent ):
def __init__( self, parent, canvas_type ):
QW.QWidget.__init__( self, parent )
self._canvas_type = canvas_type
# If I do not set this, macOS goes 100% CPU endless repaint events!
# My guess is it due to the borked layout
# it means 'I guarantee to cover my whole viewport with pixels, no need for automatic background clear'
@ -5170,11 +5295,16 @@ class MediaContainer( QW.QWidget ):
self._animation_window = Animation( self )
self._animation_bar = AnimationBar( self )
self._volume_control = ClientGUIMediaControls.VolumeControl( self, self._canvas_type, direction = 'up' )
self._mpv_window = None
self._static_image_window = StaticImage( self )
self._volume_control.adjustSize()
self._volume_control.setCursor( QG.Qt.ArrowCursor )
self._animation_window.hide()
self._animation_bar.hide()
self._volume_control.hide()
self._static_image_window.hide()
self._embed_button.hide()
@ -5277,6 +5407,8 @@ class MediaContainer( QW.QWidget ):
self._mpv_window = HG.client_controller.gui.GetMPVWidget( self )
self._mpv_window.SetCanvasType( self._canvas_type )
self._media_window = self._mpv_window
@ -5287,12 +5419,23 @@ class MediaContainer( QW.QWidget ):
self._animation_bar.SetMediaAndWindow( self._media, self._media_window )
if self._mpv_window is not None and self._media.HasAudio():
self._volume_control.show()
else:
self._volume_control.hide()
self._animation_bar.show()
else:
self._HideAnimationBar()
self._volume_control.hide()
if old_media_window is not None and destroy_old_media_window:
@ -5328,9 +5471,24 @@ class MediaContainer( QW.QWidget ):
media_height -= animated_scanbar_height
self._animation_bar.setFixedSize( QP.TupleToQSize( ( my_width, animated_scanbar_height ) ) )
if self._volume_control.isVisibleTo( self ):
volume_width = self._volume_control.width()
else:
volume_width = 0
self._animation_bar.setFixedSize( QP.TupleToQSize( ( my_width - volume_width, animated_scanbar_height ) ) )
self._animation_bar.move( QP.TupleToQPoint( ( 0, my_height - animated_scanbar_height ) ) )
if self._volume_control.isVisibleTo( self ):
self._volume_control.setFixedSize( QP.TupleToQSize( ( volume_width, animated_scanbar_height ) ) )
self._volume_control.move( QP.TupleToQPoint( ( self._animation_bar.width(), my_height - animated_scanbar_height ) ) )
self._media_window.setFixedSize( QP.TupleToQSize( ( media_width, media_height ) ) )
self._media_window.move( QP.TupleToQPoint( ( 0, 0 ) ) )
@ -5432,6 +5590,22 @@ class MediaContainer( QW.QWidget ):
def paintEvent( self, event ):
painter = None
# hackery dackery doo to deal with non-redrawing single-pixel border around the real widget
# we'll fix this when we fix the larger layout/repaint issue
if self._volume_control.isVisible():
painter = QG.QPainter( self )
background_colour = HG.client_controller.new_options.GetColour( CC.COLOUR_MEDIA_BACKGROUND )
painter.setBrush( QG.QBrush( background_colour ) )
painter.setPen( QC.Qt.NoPen )
painter.drawRect( self._volume_control.geometry() )
if self._media_window is not None and self._media_window.isVisible():
return
@ -5441,7 +5615,10 @@ class MediaContainer( QW.QWidget ):
# mpv embed fun aggravates this
# so instead we do an explicit repaint after the hide and before the new show, to clear our window
painter = QG.QPainter( self )
if painter is None:
painter = QG.QPainter( self )
background_colour = HG.client_controller.new_options.GetColour( CC.COLOUR_MEDIA_BACKGROUND )
@ -5504,6 +5681,8 @@ class MediaContainer( QW.QWidget ):
self._HideAnimationBar()
self._volume_control.hide()
self._DestroyOrHideThisMediaWindow( self._media_window )
self._media_window = None
@ -5546,6 +5725,8 @@ class MediaContainer( QW.QWidget ):
self._HideAnimationBar()
self._volume_control.hide()
self._DestroyOrHideThisMediaWindow( self._media_window )
self._media_window = None

View File

@ -25,6 +25,14 @@ from qtpy import QtWidgets as QW
from qtpy import QtGui as QG
from . import QtPorting as QP
CANVAS_MEDIA_VIEWER = 0
CANVAS_PREVIEW = 1
canvas_str_lookup = {}
canvas_str_lookup[ CANVAS_MEDIA_VIEWER ] = 'media viewer'
canvas_str_lookup[ CANVAS_PREVIEW ] = 'preview'
def WrapInGrid( parent, rows, expand_text = False, add_stretch_at_end = True ):
gridbox = QP.GridLayout( cols = 2 )

View File

@ -361,9 +361,20 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
transformation_type = ClientParsing.STRING_TRANSFORMATION_APPEND_TEXT
data = ' extra text'
try:
string_converter = self._GetValue()
example_string_at_this_point = string_converter.Convert( self._example_string.text() )
except:
example_string_at_this_point = self._example_string.text()
with ClientGUITopLevelWindows.DialogEdit( self, 'edit transformation', frame_key = 'deeply_nested_dialog' ) as dlg:
panel = self._TransformationPanel( dlg, transformation_type, data )
panel = self._TransformationPanel( dlg, transformation_type, data, example_string_at_this_point )
dlg.SetPanel( panel )
@ -504,9 +515,20 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
( number, transformation_type, data ) = enumerated_transformation
try:
string_converter = self._GetValue()
example_string_at_this_point = string_converter.Convert( self._example_string.text(), number - 1 )
except:
example_string_at_this_point = self._example_string.text()
with ClientGUITopLevelWindows.DialogEdit( self, 'edit transformation', frame_key = 'deeply_nested_dialog' ) as dlg:
panel = self._TransformationPanel( dlg, transformation_type, data )
panel = self._TransformationPanel( dlg, transformation_type, data, example_string_at_this_point )
dlg.SetPanel( panel )
@ -643,7 +665,7 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
class _TransformationPanel( ClientGUIScrolledPanels.EditPanel ):
def __init__( self, parent, transformation_type, data ):
def __init__( self, parent, transformation_type, data, example_text ):
ClientGUIScrolledPanels.EditPanel.__init__( self, parent )
@ -654,6 +676,21 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
self._transformation_type.addItem( ClientParsing.transformation_type_str_lookup[ t_type], t_type )
self._example_string = QW.QLineEdit( self )
min_width = ClientGUIFunctions.ConvertTextToPixelWidth( self._example_string, 96 )
self._example_string.setMinimumWidth( min_width )
self._example_string.setText( example_text )
self._example_transformation = QW.QLineEdit( self )
self._update_example = ClientGUICommon.BetterButton( self, 'update example', self._UpdateExampleText )
self._example_string.setEnabled( False )
self._example_transformation.setEnabled( False )
self._data_text = QW.QLineEdit( self )
self._data_number = QP.MakeQSpinBox( self, min=0, max=65535 )
self._data_encoding = ClientGUICommon.BetterChoice( self )
@ -726,6 +763,13 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
rows = []
rows.append( ( 'example string: ', self._example_string ) )
rows.append( ( 'transformed string: ', self._example_transformation ) )
example_gridbox = ClientGUICommon.WrapInGrid( self, rows )
rows = []
rows.append( ( 'string data: ', self._data_text ) )
rows.append( ( 'number data: ', self._data_number ) )
rows.append( ( 'encoding data: ', self._data_encoding ) )
@ -740,6 +784,8 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
vbox = QP.VBoxLayout()
QP.AddToLayout( vbox, example_gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
QP.AddToLayout( vbox, self._update_example, CC.FLAGS_LONE_BUTTON )
QP.AddToLayout( vbox, self._transformation_type, CC.FLAGS_EXPAND_PERPENDICULAR )
QP.AddToLayout( vbox, gridbox, CC.FLAGS_EXPAND_SIZER_PERPENDICULAR )
@ -752,6 +798,8 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
self._data_timezone_decode.currentIndexChanged.connect( self._UpdateDataControls )
self._data_timezone_encode.currentIndexChanged.connect( self._UpdateDataControls )
self._UpdateExampleText()
def _UpdateDataControls( self ):
@ -808,6 +856,26 @@ class EditStringConverterPanel( ClientGUIScrolledPanels.EditPanel ):
def _UpdateExampleText( self ):
try:
transformations = [ self.GetValue() ]
example_string = self._example_string.text()
string_converter = ClientParsing.StringConverter( transformations, example_string )
example_transformation = string_converter.Convert( example_string )
self._example_transformation.setText( example_transformation )
except Exception as e:
self._example_transformation.setText( str( e ) )
def GetValue( self ):
transformation_type = self._transformation_type.GetValue()

View File

@ -117,7 +117,7 @@ class DialogManageRatings( ClientGUIDialogs.Dialog ):
#
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media' ] )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'global', 'media' ] )
def EventOK( self ):

View File

@ -6,6 +6,7 @@ from . import ClientGUICommon
from . import ClientGUIDialogs
from . import ClientGUIFunctions
from . import ClientGUIListBoxes
from . import ClientGUIMediaControls
from . import ClientGUIMenus
from . import ClientGUIMPV
from . import ClientGUITopLevelWindows
@ -106,6 +107,21 @@ class FullscreenHoverFrame( QW.QFrame ):
focus_is_good = focus_is_on_descendant and focus_has_right_window_type
mouse_is_over_self_or_child = False
for tlw in QW.QApplication.topLevelWidgets():
if tlw == self or ClientGUIFunctions.IsQtAncestor( tlw, self, through_tlws = True ):
if tlw.underMouse():
mouse_is_over_self_or_child = True
break
new_options = HG.client_controller.new_options
if self._always_on_top:
@ -192,12 +208,13 @@ class FullscreenHoverFrame( QW.QFrame ):
mouse_is_near_animation_bar = self._my_canvas.MouseIsNearAnimationBar()
mouse_is_over_something_important = mouse_is_near_animation_bar # this used to have the flash media window test to ensure mouse over flash window hid hovers going over it
# this used to have the flash media window test to ensure mouse over flash window hid hovers going over it
mouse_is_over_something_else_important = mouse_is_near_animation_bar
hide_focus_is_good = focus_is_good or current_focus_tlw is None # don't hide if focus is either gone to another problem or temporarily sperging-out due to a click-transition or similar
ready_to_show = in_position and not mouse_is_over_something_important and focus_is_good and not dialog_open and not menu_open
ready_to_hide = not menu_open and ( not in_position or dialog_open or not hide_focus_is_good )
ready_to_show = in_position and not mouse_is_over_something_else_important and focus_is_good and not dialog_open and not menu_open
ready_to_hide = not menu_open and not mouse_is_over_self_or_child and ( not in_position or dialog_open or not hide_focus_is_good )
def get_logic_report_string():
@ -561,8 +578,6 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
HG.client_controller.sub( self, 'ProcessContentUpdates', 'content_updates_gui' )
HG.client_controller.sub( self, 'SetCurrentZoom', 'canvas_new_zoom' )
HG.client_controller.sub( self, 'SetIndexString', 'canvas_new_index_string' )
HG.client_controller.sub( self, 'UpdateGlobalAudioMute', 'new_global_audio_mute' )
HG.client_controller.sub( self, 'UpdateGlobalAudioVolume', 'new_global_audio_volume' )
def _Archive( self ):
@ -579,13 +594,6 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
HG.client_controller.pub( 'canvas_application_command', command, self._canvas_key )
def _FlipGlobalMute( self ):
HG.client_controller.new_options.FlipBoolean( 'global_audio_mute' )
HG.client_controller.pub( 'new_global_audio_mute' )
def _GetIdealSizeAndPosition( self ):
# clip this and friends to availableScreenGeometry for size and position, not rely 100% on parent
@ -659,27 +667,11 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
zoom_switch = ClientGUICommon.BetterBitmapButton( self, CC.GlobalPixmaps.zoom_switch, HG.client_controller.pub, 'canvas_application_command', ClientData.ApplicationCommand( CC.APPLICATION_COMMAND_TYPE_SIMPLE, 'switch_between_100_percent_and_canvas_zoom' ), self._canvas_key )
zoom_switch.SetToolTipWithShortcuts( 'zoom switch', 'switch_between_100_percent_and_canvas_zoom' )
self._global_audio_volume_slider = QW.QSlider( self )
self._global_audio_volume_slider.setOrientation( QC.Qt.Horizontal )
self._global_audio_volume_slider.setTickInterval( 1 )
self._global_audio_volume_slider.setTickPosition( QW.QSlider.TicksBothSides )
self._global_audio_volume_slider.setRange( 0, 100 )
self._global_audio_volume_slider.setValue( HG.client_controller.new_options.GetInteger( 'global_audio_volume' ) )
self._global_audio_volume_slider.valueChanged.connect( self._VolumeSliderMoved )
self._global_audio_mute_checkbox = QW.QCheckBox( 'mute', self )
self._global_audio_mute_checkbox.setChecked( HG.client_controller.new_options.GetBoolean( 'global_audio_mute' ) )
self._global_audio_mute_checkbox.clicked.connect( self._FlipGlobalMute )
self._volume_control = ClientGUIMediaControls.VolumeControl( self, ClientGUICommon.CANVAS_MEDIA_VIEWER )
if not ClientGUIMPV.MPV_IS_AVAILABLE:
self._global_audio_mute_checkbox.hide()
self._global_audio_volume_slider.hide()
self._volume_control.hide()
shortcuts = ClientGUICommon.BetterBitmapButton( self, CC.GlobalPixmaps.keyboard, self._ShowShortcutMenu )
@ -709,8 +701,7 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
QP.AddToLayout( self._top_hbox, zoom_in, CC.FLAGS_VCENTER )
QP.AddToLayout( self._top_hbox, zoom_out, CC.FLAGS_VCENTER )
QP.AddToLayout( self._top_hbox, zoom_switch, CC.FLAGS_VCENTER )
QP.AddToLayout( self._top_hbox, self._global_audio_volume_slider, CC.FLAGS_VCENTER )
QP.AddToLayout( self._top_hbox, self._global_audio_mute_checkbox, CC.FLAGS_VCENTER )
QP.AddToLayout( self._top_hbox, self._volume_control, CC.FLAGS_VCENTER )
QP.AddToLayout( self._top_hbox, shortcuts, CC.FLAGS_VCENTER )
QP.AddToLayout( self._top_hbox, fullscreen_switch, CC.FLAGS_VCENTER )
QP.AddToLayout( self._top_hbox, open_externally, CC.FLAGS_VCENTER )
@ -842,13 +833,6 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
HG.client_controller.PopupMenu( self, menu )
def _VolumeSliderMoved( self ):
HG.client_controller.new_options.SetInteger( 'global_audio_volume', self._global_audio_volume_slider.value() )
HG.client_controller.pub( 'new_global_audio_volume' )
def EventDragButton( self ):
if self._current_media is None:
@ -947,16 +931,6 @@ class FullscreenHoverFrameTop( FullscreenHoverFrame ):
def UpdateGlobalAudioMute( self ):
self._global_audio_mute_checkbox.setChecked( HG.client_controller.new_options.GetBoolean( 'global_audio_mute' ) )
def UpdateGlobalAudioVolume( self ):
self._global_audio_volume_slider.setValue( HG.client_controller.new_options.GetInteger( 'global_audio_volume' ) )
class FullscreenHoverFrameTopArchiveDeleteFilter( FullscreenHoverFrameTop ):
def _Archive( self ):

View File

@ -1,3 +1,5 @@
from . import ClientGUICommon
from . import ClientGUIMediaControls
from . import HydrusConstants as HC
from . import HydrusData
from . import HydrusGlobals as HG
@ -14,7 +16,7 @@ try:
MPV_IS_AVAILABLE = True
except:
except Exception as e:
MPV_IS_AVAILABLE = False
@ -39,6 +41,8 @@ class mpvWidget( QW.QWidget ):
QW.QWidget.__init__( self, parent )
self._canvas_type = ClientGUICommon.CANVAS_PREVIEW
# This is necessary since PyQT stomps over the locale settings needed by libmpv.
# This needs to happen after importing PyQT before creating the first mpv.MPV instance.
locale.setlocale( locale.LC_NUMERIC, 'C' )
@ -85,10 +89,70 @@ class mpvWidget( QW.QWidget ):
self.destroyed.connect( self._player.terminate )
HG.client_controller.sub( self, 'UpdateGlobalAudioMute', 'new_global_audio_mute' )
HG.client_controller.sub( self, 'UpdateGlobalAudioVolume', 'new_global_audio_volume' )
HG.client_controller.sub( self, 'UpdateAudioMute', 'new_audio_mute' )
HG.client_controller.sub( self, 'UpdateAudioVolume', 'new_audio_volume' )
def _GetAudioOptionNames( self ):
if self._canvas_type == ClientGUICommon.CANVAS_MEDIA_VIEWER:
if HG.client_controller.new_options.GetBoolean( 'media_viewer_uses_its_own_audio_volume' ):
return ClientGUIMediaControls.volume_types_to_option_names[ ClientGUIMediaControls.AUDIO_MEDIA_VIEWER ]
elif self._canvas_type == ClientGUICommon.CANVAS_PREVIEW:
if HG.client_controller.new_options.GetBoolean( 'preview_uses_its_own_audio_volume' ):
return ClientGUIMediaControls.volume_types_to_option_names[ ClientGUIMediaControls.AUDIO_PREVIEW ]
return ClientGUIMediaControls.volume_types_to_option_names[ ClientGUIMediaControls.AUDIO_GLOBAL ]
def _GetCorrectCurrentMute( self ):
( global_mute_option_name, global_volume_option_name ) = ClientGUIMediaControls.volume_types_to_option_names[ ClientGUIMediaControls.AUDIO_GLOBAL ]
mute_option_name = global_mute_option_name
if self._canvas_type == ClientGUICommon.CANVAS_MEDIA_VIEWER:
( mute_option_name, volume_option_name ) = ClientGUIMediaControls.volume_types_to_option_names[ ClientGUIMediaControls.AUDIO_MEDIA_VIEWER ]
elif self._canvas_type == ClientGUICommon.CANVAS_PREVIEW:
( mute_option_name, volume_option_name ) = ClientGUIMediaControls.volume_types_to_option_names[ ClientGUIMediaControls.AUDIO_PREVIEW ]
return HG.client_controller.new_options.GetBoolean( mute_option_name ) or HG.client_controller.new_options.GetBoolean( global_mute_option_name )
def _GetCorrectCurrentVolume( self ):
( mute_option_name, volume_option_name ) = ClientGUIMediaControls.volume_types_to_option_names[ ClientGUIMediaControls.AUDIO_GLOBAL ]
if self._canvas_type == ClientGUICommon.CANVAS_MEDIA_VIEWER:
if HG.client_controller.new_options.GetBoolean( 'media_viewer_uses_its_own_audio_volume' ):
( mute_option_name, volume_option_name ) = ClientGUIMediaControls.volume_types_to_option_names[ ClientGUIMediaControls.AUDIO_MEDIA_VIEWER ]
elif self._canvas_type == ClientGUICommon.CANVAS_PREVIEW:
if HG.client_controller.new_options.GetBoolean( 'preview_uses_its_own_audio_volume' ):
( mute_option_name, volume_option_name ) = ClientGUIMediaControls.volume_types_to_option_names[ ClientGUIMediaControls.AUDIO_PREVIEW ]
return HG.client_controller.new_options.GetInteger( volume_option_name )
def GetAnimationBarStatus( self ):
buffer_indices = None
@ -243,7 +307,12 @@ class mpvWidget( QW.QWidget ):
self._player.pause = False
def SetCanvasType( self, canvas_type ):
self._canvas_type = canvas_type
def SetMedia( self, media, start_paused = False ):
self._media = media
@ -254,7 +323,14 @@ class mpvWidget( QW.QWidget ):
if len( self._player.playlist ) > 0:
self._player.command( 'playlist-remove', 'current' )
try:
self._player.command( 'playlist-remove', 'current' )
except:
pass # sometimes happens after an error--screw it
else:
@ -281,8 +357,8 @@ class mpvWidget( QW.QWidget ):
HydrusData.ShowException( e )
self._player.volume = HG.client_controller.new_options.GetInteger( 'global_audio_volume' )
self._player.mute = HG.client_controller.new_options.GetBoolean( 'global_audio_mute' )
self._player.volume = self._GetCorrectCurrentVolume()
self._player.mute = self._GetCorrectCurrentMute()
self._player.pause = start_paused
@ -292,12 +368,12 @@ class mpvWidget( QW.QWidget ):
self.SetMedia( None )
def UpdateGlobalAudioMute( self ):
def UpdateAudioMute( self ):
self._player.mute = HG.client_controller.new_options.GetBoolean( 'global_audio_mute' )
self._player.mute = self._GetCorrectCurrentMute()
def UpdateGlobalAudioVolume( self ):
def UpdateAudioVolume( self ):
self._player.volume = HG.client_controller.new_options.GetInteger( 'global_audio_volume' )
self._player.volume = self._GetCorrectCurrentVolume()

View File

@ -0,0 +1,289 @@
from . import ClientConstants as CC
from . import ClientData
from . import ClientGUICommon
from . import ClientGUIFunctions
from . import ClientGUIMenus
from . import ClientGUIMPV
from . import ClientGUITopLevelWindows
from . import HydrusConstants as HC
from . import HydrusData
from . import HydrusGlobals as HG
from qtpy import QtCore as QC
from qtpy import QtWidgets as QW
from qtpy import QtGui as QG
from . import QtPorting as QP
AUDIO_GLOBAL = 0
AUDIO_MEDIA_VIEWER = 1
AUDIO_PREVIEW = 2
volume_types_str_lookup = {}
volume_types_str_lookup[ AUDIO_GLOBAL ] = 'global'
volume_types_str_lookup[ AUDIO_MEDIA_VIEWER ] = 'media viewer'
volume_types_str_lookup[ AUDIO_PREVIEW ] = 'preview'
volume_types_to_option_names = {}
volume_types_to_option_names[ AUDIO_GLOBAL ] = ( 'global_audio_mute', 'global_audio_volume' )
volume_types_to_option_names[ AUDIO_MEDIA_VIEWER ] = ( 'media_viewer_audio_mute', 'media_viewer_audio_volume' )
volume_types_to_option_names[ AUDIO_PREVIEW ] = ( 'preview_audio_mute', 'preview_audio_volume' )
def ChangeVolume( volume_type, volume ):
( mute_option_name, volume_option_name ) = volume_types_to_option_names[ volume_type ]
HG.client_controller.new_options.SetInteger( volume_option_name, volume )
HG.client_controller.pub( 'new_audio_volume' )
def FlipMute( volume_type ):
( mute_option_name, volume_option_name ) = volume_types_to_option_names[ volume_type ]
HG.client_controller.new_options.FlipBoolean( mute_option_name )
HG.client_controller.pub( 'new_audio_mute' )
def SetMute( volume_type, mute ):
( mute_option_name, volume_option_name ) = volume_types_to_option_names[ volume_type ]
HG.client_controller.new_options.SetBoolean( mute_option_name, mute )
HG.client_controller.pub( 'new_audio_mute' )
class AudioMuteButton( ClientGUICommon.BetterBitmapButton ):
def __init__( self, parent, volume_type ):
self._volume_type = volume_type
pixmap = self._GetCorrectPixmap()
ClientGUICommon.BetterBitmapButton.__init__( self, parent, pixmap, FlipMute, self._volume_type )
HG.client_controller.sub( self, 'UpdateMute', 'new_audio_mute' )
def _GetCorrectPixmap( self ):
( mute_option_name, volume_option_name ) = volume_types_to_option_names[ self._volume_type ]
if HG.client_controller.new_options.GetBoolean( mute_option_name ):
pixmap = CC.GlobalPixmaps.mute
else:
pixmap = CC.GlobalPixmaps.sound
return pixmap
def UpdateMute( self ):
pixmap = self._GetCorrectPixmap()
ClientGUIFunctions.SetBitmapButtonBitmap( self, pixmap )
class VolumeControl( QW.QWidget ):
def __init__( self, parent, canvas_type, direction = 'down' ):
QW.QWidget.__init__( self, parent )
self._canvas_type = canvas_type
self._global_mute = AudioMuteButton( self, AUDIO_GLOBAL )
self._global_mute.setToolTip( 'Global mute/unmute' )
vbox = QP.VBoxLayout( margin = 0, spacing = 0 )
QP.AddToLayout( vbox, self._global_mute, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS )
self.setLayout( vbox )
self._popup_window = self._PopupWindow( self, canvas_type, direction = direction )
def enterEvent( self, event ):
if not self.isVisible():
return
self._popup_window.DoShowHide()
event.ignore()
def leaveEvent( self, event ):
if not self.isVisible():
return
self._popup_window.DoShowHide()
event.ignore()
class _PopupWindow( QW.QFrame ):
def __init__( self, parent, canvas_type, direction = 'down' ):
QW.QFrame.__init__( self, parent )
self._canvas_type = canvas_type
self._direction = direction
self.setWindowFlags( QC.Qt.Tool | QC.Qt.FramelessWindowHint )
self.setAttribute( QC.Qt.WA_ShowWithoutActivating )
if self._canvas_type == ClientGUICommon.CANVAS_MEDIA_VIEWER:
option_to_use = 'media_viewer_uses_its_own_audio_volume'
volume_type = AUDIO_MEDIA_VIEWER
else:
option_to_use = 'preview_uses_its_own_audio_volume'
volume_type = AUDIO_PREVIEW
self._specific_mute = AudioMuteButton( self, volume_type )
self._specific_mute.setToolTip( 'Mute/unmute: {}'.format( ClientGUICommon.canvas_str_lookup[ self._canvas_type ] ) )
if HG.client_controller.new_options.GetBoolean( option_to_use ):
slider_volume_type = volume_type
else:
slider_volume_type = AUDIO_GLOBAL
self._volume = VolumeSlider( self, slider_volume_type )
vbox = QP.VBoxLayout()
if self._direction == 'down':
QP.AddToLayout( vbox, self._specific_mute, CC.FLAGS_SMALL_INDENT )
QP.AddToLayout( vbox, self._volume, CC.FLAGS_SMALL_INDENT )
else:
QP.AddToLayout( vbox, self._volume, CC.FLAGS_SMALL_INDENT )
QP.AddToLayout( vbox, self._specific_mute, CC.FLAGS_SMALL_INDENT )
vbox.setAlignment( self._volume, QC.Qt.AlignHCenter )
vbox.setAlignment( self._specific_mute, QC.Qt.AlignHCenter )
self.setLayout( vbox )
self.hide()
self.adjustSize()
def DoShowHide( self ):
parent = self.parent()
horizontal_offset = ( self.width() - parent.width() ) // 2
if self._direction == 'down':
pos = parent.mapToGlobal( parent.rect().bottomLeft() )
else:
pos = parent.mapToGlobal( parent.rect().topLeft() - self.rect().bottomLeft() )
pos.setX( pos.x() - horizontal_offset )
self.move( pos )
# QWidget.underMouse() doesn't work on the border edge and hence gives flicker, so just do it manually
cursor_pos = QG.QCursor.pos()
over_parent = parent.rect().contains( parent.mapFromGlobal( cursor_pos ) )
over_me = self.rect().contains( self.mapFromGlobal( cursor_pos ) )
should_show = over_parent or over_me
if should_show:
self.show()
else:
self.hide()
def leaveEvent( self, event ):
if not self.isVisible():
return
self.DoShowHide()
event.ignore()
class VolumeSlider( QW.QSlider ):
def __init__( self, parent, volume_type ):
QW.QSlider.__init__( self, parent )
self._volume_type = volume_type
self.setOrientation( QC.Qt.Vertical )
self.setTickInterval( 1 )
self.setTickPosition( QW.QSlider.TicksBothSides )
self.setRange( 0, 100 )
volume = self._GetCorrectValue()
self.setValue( volume )
self.valueChanged.connect( self._VolumeSliderMoved )
def _GetCorrectValue( self ):
( mute_option_name, volume_option_name ) = volume_types_to_option_names[ self._volume_type ]
return HG.client_controller.new_options.GetInteger( volume_option_name )
def _VolumeSliderMoved( self ):
ChangeVolume( self._volume_type, self.value() )
def UpdateMute( self ):
volume = self._GetCorrectValue()
self.setValue( volume )

View File

@ -12,6 +12,7 @@ from . import ClientGUIFunctions
from . import ClientGUIImport
from . import ClientGUIListBoxes
from . import ClientGUIListCtrl
from . import ClientGUIMediaControls
from . import ClientGUIPanels
from . import ClientGUIPredicates
from . import ClientGUIScrolledPanels
@ -1517,7 +1518,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._listbook.AddPage( 'speed and memory', 'speed and memory', self._SpeedAndMemoryPanel( self._listbook, self._new_options ) )
self._listbook.AddPage( 'maintenance and processing', 'maintenance and processing', self._MaintenanceAndProcessingPanel( self._listbook ) )
self._listbook.AddPage( 'media', 'media', self._MediaPanel( self._listbook ) )
self._listbook.AddPage( 'audio and duration', 'audio and duration', self._AudioAndDurationPanel( self._listbook, self._new_options ) )
self._listbook.AddPage( 'audio', 'audio', self._AudioPanel( self._listbook, self._new_options ) )
self._listbook.AddPage( 'default system predicates', 'default system predicates', self._DefaultFileSystemPredicatesPanel( self._listbook, self._new_options ) )
self._listbook.AddPage( 'colours', 'colours', self._ColoursPanel( self._listbook ) )
self._listbook.AddPage( 'regex favourites', 'regex favourites', self._RegexPanel( self._listbook ) )
@ -1540,7 +1541,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self.widget().setLayout( vbox )
class _AudioAndDurationPanel( QW.QWidget ):
class _AudioPanel( QW.QWidget ):
def __init__( self, parent, new_options ):
@ -1548,13 +1549,22 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._new_options = new_options
self._has_duration_label = QW.QLineEdit( self )
#self._media_viewer_uses_its_own_audio_volume = QW.QCheckBox( self )
self._preview_uses_its_own_audio_volume = QW.QCheckBox( self )
self._has_audio_label = QW.QLineEdit( self )
#
self._has_duration_label.setText( self._new_options.GetString( 'has_duration_label' ) )
tt = 'If unchecked, this media canvas will use the \'global\' audio volume slider. If checked, this media canvas will have its own separate one.'
tt += os.linesep * 2
tt += 'Keep this on if you would like the preview viewer to be quieter than the main media viewer.'
#self._media_viewer_uses_its_own_audio_volume.setChecked( self._new_options.GetBoolean( 'media_viewer_uses_its_own_audio_volume' ) )
self._preview_uses_its_own_audio_volume.setChecked( self._new_options.GetBoolean( 'preview_uses_its_own_audio_volume' ) )
#self._media_viewer_uses_its_own_audio_volume.setToolTip( tt )
self._preview_uses_its_own_audio_volume.setToolTip( tt )
self._has_audio_label.setText( self._new_options.GetString( 'has_audio_label' ) )
@ -1564,7 +1574,8 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
rows = []
rows.append( ( 'Label for files with duration: ', self._has_duration_label ) )
rows.append( ( 'The preview window has its own volume: ', self._preview_uses_its_own_audio_volume ) )
#rows.append( ( 'The media viewer has its own volume: ', self._media_viewer_uses_its_own_audio_volume ) )
rows.append( ( 'Label for files with audio: ', self._has_audio_label ) )
gridbox = ClientGUICommon.WrapInGrid( self, rows )
@ -1577,7 +1588,9 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
def UpdateOptions( self ):
self._new_options.SetString( 'has_duration_label', self._has_duration_label.text() )
#self._new_options.SetBoolean( 'media_viewer_uses_its_own_audio_volume', self._media_viewer_uses_its_own_audio_volume.isChecked() )
self._new_options.SetBoolean( 'preview_uses_its_own_audio_volume', self._preview_uses_its_own_audio_volume.isChecked() )
self._new_options.SetString( 'has_audio_label', self._has_audio_label.text() )
@ -4182,6 +4195,9 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
self._apply_all_parents_to_all_services = QW.QCheckBox( general_panel )
self._apply_all_siblings_to_all_services = QW.QCheckBox( general_panel )
self._apply_all_parents_to_all_services.setToolTip( 'If checked, all services will apply their tag parents to each other. If unchecked, services will only apply tag parents to themselves.' )
self._apply_all_siblings_to_all_services.setToolTip( 'If checked, all services will apply their tag siblings to each other. If unchecked, services will only apply tag siblings to themselves. In case of conflict, local tag services have precedence.' )
#
favourites_panel = ClientGUICommon.StaticBox( self, 'favourite tags' )
@ -4238,7 +4254,7 @@ class ManageOptionsPanel( ClientGUIScrolledPanels.ManagePanel ):
rows.append( ( 'Default tag sort: ', self._default_tag_sort ) )
rows.append( ( 'By default, search \'all known files\' in \'write\' tag autocomplete inputs: ', self._show_all_tags_in_autocomplete ) )
rows.append( ( 'By default, select the first tag result with actual count in write-autocomplete: ', self._ac_select_first_with_count ) )
rows.append( ( 'Suggest all parents for all services: ', self._apply_all_parents_to_all_services ) )
rows.append( ( 'Apply all parents for all services: ', self._apply_all_parents_to_all_services ) )
rows.append( ( 'Apply all siblings to all services (local siblings have precedence): ', self._apply_all_siblings_to_all_services ) )
gridbox = ClientGUICommon.WrapInGrid( general_panel, rows )
@ -6027,7 +6043,7 @@ class ManageURLsPanel( ClientGUIScrolledPanels.ManagePanel ):
self.widget().setLayout( vbox )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media', 'main_gui' ] )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'global', 'media', 'main_gui' ] )
QP.CallAfter( self._SetSearchFocus )

View File

@ -1110,7 +1110,7 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
HG.client_controller.sub( self, 'CanvasHasNewMedia', 'canvas_new_display_media' )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'media', 'main_gui' ] )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'global', 'media', 'main_gui' ] )
self._tag_repositories.currentChanged.connect( self.EventServiceChanged )
@ -1437,7 +1437,7 @@ class ManageTagsPanel( ClientGUIScrolledPanels.ManagePanel ):
#
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'main_gui' ] )
self._my_shortcut_handler = ClientGUIShortcuts.ShortcutsHandler( self, [ 'global', 'main_gui' ] )
self.setLayout( hbox )

View File

@ -211,6 +211,10 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
self._dictionary[ 'booleans' ][ 'autocomplete_float_frames' ] = False
self._dictionary[ 'booleans' ][ 'global_audio_mute' ] = False
self._dictionary[ 'booleans' ][ 'media_viewer_audio_mute' ] = False
self._dictionary[ 'booleans' ][ 'media_viewer_uses_its_own_audio_volume' ] = False
self._dictionary[ 'booleans' ][ 'preview_audio_mute' ] = False
self._dictionary[ 'booleans' ][ 'preview_uses_its_own_audio_volume' ] = True
#
@ -305,6 +309,8 @@ class ClientOptions( HydrusSerialisable.SerialisableBase ):
self._dictionary[ 'integers' ][ 'video_thumbnail_percentage_in' ] = 35
self._dictionary[ 'integers' ][ 'global_audio_volume' ] = 70
self._dictionary[ 'integers' ][ 'media_viewer_audio_volume' ] = 70
self._dictionary[ 'integers' ][ 'preview_audio_volume' ] = 70
self._dictionary[ 'integers' ][ 'duplicate_comparison_score_higher_jpeg_quality' ] = 10
self._dictionary[ 'integers' ][ 'duplicate_comparison_score_much_higher_jpeg_quality' ] = 20

View File

@ -2871,6 +2871,11 @@ class StringConverter( HydrusSerialisable.SerialisableBase ):
for ( i, transformation ) in enumerate( self.transformations ):
if max_steps_allowed is not None and i >= max_steps_allowed:
return s
try:
( transformation_type, data ) = transformation
@ -3027,11 +3032,6 @@ class StringConverter( HydrusSerialisable.SerialisableBase ):
raise HydrusExceptions.StringConvertException( 'ERROR: Could not apply "' + self.TransformationToString( transformation ) + '" to string "' + repr( s ) + '":' + str( e ) )
if max_steps_allowed is not None and i + 1 >= max_steps_allowed:
return s
return s

View File

@ -70,7 +70,7 @@ options = {}
# Misc
NETWORK_VERSION = 18
SOFTWARE_VERSION = 382
SOFTWARE_VERSION = 383
CLIENT_API_VERSION = 11
SERVER_THUMBNAIL_DIMENSIONS = ( 200, 200 )

View File

@ -950,7 +950,7 @@ def AddToLayout( layout, item, flag = None, alignment = None, sizePolicy = None
item.setSizePolicy( sizePolicy[0], sizePolicy[1] )
expand_both_ways = flag in ( CC.FLAGS_EXPAND_BOTH_WAYS, CC.FLAGS_EXPAND_BOTH_WAYS_POLITE, CC.FLAGS_EXPAND_BOTH_WAYS_SHY )
expand_both_ways = flag in ( CC.FLAGS_EXPAND_BOTH_WAYS, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS, CC.FLAGS_EXPAND_BOTH_WAYS_POLITE, CC.FLAGS_EXPAND_BOTH_WAYS_SHY )
zero_border = False
# This is kind of a mess right now, adjustments might be needed
@ -1006,10 +1006,6 @@ def AddToLayout( layout, item, flag = None, alignment = None, sizePolicy = None
#item.setContentsMargins( 0, 0, 0, 0 )
elif flag == CC.FLAGS_EXPAND_SIZER_BOTH_WAYS:
zero_border = True
if isinstance( layout, QW.QVBoxLayout ) or isinstance( layout, QW.QHBoxLayout ):
layout.setStretchFactor( item, 5 )
#item.setContentsMargins( 0, 0, 0, 0 )
@ -1057,7 +1053,7 @@ def AddToLayout( layout, item, flag = None, alignment = None, sizePolicy = None
if isinstance( layout, QW.QVBoxLayout ) or isinstance( layout, QW.QHBoxLayout ):
if flag == CC.FLAGS_EXPAND_BOTH_WAYS:
if flag in ( CC.FLAGS_EXPAND_BOTH_WAYS, CC.FLAGS_EXPAND_SIZER_BOTH_WAYS ):
stretch_factor = 5

View File

@ -5,16 +5,16 @@ autoload-files=no
access-references=no
rescan-external-files=keep-selection
# use high quality gpu by default
# use high quality gpu, may cause errors for some files
profile=gpu-hq
# profile=gpu-hq
# enable hardware acceleration
# may improve performance, may cause errors
# you may find hwdec=auto-copy looks better/smoother for you
hwdec=auto
hwdec-codecs=all
# hwdec=auto
# hwdec-codecs=all
# needed for SVP

BIN
static/mute.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 474 B