From 2da8e84bba8a782fd0324deb0b0bc6c4d5e53714 Mon Sep 17 00:00:00 2001 From: Etoh Date: Sun, 7 May 2017 13:20:28 +0100 Subject: [PATCH] Add chat input config --- buildPy2exe.py | 2 + resources/error.png | Bin 0 -> 666 bytes resources/syncplayintf.lua | 106 ++++++++++++++++++++++++----- resources/user_comment.png | Bin 0 -> 743 bytes syncplay/constants.py | 11 +++ syncplay/messages.py | 1 + syncplay/messages_de.py | 20 ++++++ syncplay/messages_en.py | 20 ++++++ syncplay/messages_ru.py | 11 +++ syncplay/players/mpv.py | 9 +++ syncplay/ui/ConfigurationGetter.py | 31 ++++++++- syncplay/ui/GuiConfiguration.py | 102 +++++++++++++++++++++++++-- syncplay/utils.py | 8 +++ 13 files changed, 296 insertions(+), 25 deletions(-) create mode 100644 resources/error.png create mode 100644 resources/user_comment.png diff --git a/buildPy2exe.py b/buildPy2exe.py index 4c161cf..fef0b6f 100644 --- a/buildPy2exe.py +++ b/buildPy2exe.py @@ -657,6 +657,8 @@ guiIcons = ['resources/accept.png', 'resources/arrow_undo.png', 'resources/clock 'resources/tick.png', 'resources/lock_open.png', 'resources/empty_checkbox.png', 'resources/tick_checkbox.png', 'resources/world_explore.png', 'resources/application_get.png', 'resources/cog.png', 'resources/arrow_switch.png', 'resources/film_go.png', 'resources/world_go.png', 'resources/arrow_refresh.png', 'resources/bullet_right_grey.png', + 'resources/user_comment.png', + 'resources/error.png', 'resources/film_folder_edit.png', 'resources/film_edit.png', 'resources/folder_film.png', diff --git a/resources/error.png b/resources/error.png new file mode 100644 index 0000000000000000000000000000000000000000..628cf2dae3d419ae220c8928ac71393b480745a3 GIT binary patch literal 666 zcmV;L0%iS)P)eOSYYtbpBV}~vsBnU!_?2tr-P=|^T zED%wc9ezHgW@NMb!^uT_|SvCpFLJylbx zY%bpaTGI8IYXMN$9w<3j9VkA~NYOKEQXsj?6a9_hcwfU$acAhJhB)zb_w@MVUEy@S zX&I>K-R!bhu3?(6bHWIg$HEl7{9g>>&l_qdd+UYb(1~BCo9LptNq&8>!yoJ3Ui(i5 zRJ|XnYBklL!{@$-7=3mJ>P@1c=7Oc79e-V7yf+%lD2!I;Y&nXBZ>=B!5?CB>LvEx6 znI%n)qqi$#X#wKB(U7XP2P=+4{b@j#r%9-K(8UqtSDk>0UKzf*HM9yqMZ1D!$2MdZ zR=`U>0zhOH1XqN?nY@AQqB7)Fp4{v&dKXvb43hZKvnN8;Po;+jY*}~*Z|W9Q0W%{D z^T}Cc<|r(Su=1K=P5>Z4 zg`et&Va}tdzBS-G-ZcO)zCWpJvGQwrHZ`@wpM420ac@bI5~KkTFfGEM3sPWO8co4^fI6lPnA)Y{ef%@{+SnoUk0+dW+*{8WvF8}}l07*qoM6N<$g7cXs A&j0`b literal 0 HcmV?d00001 diff --git a/resources/syncplayintf.lua b/resources/syncplayintf.lua index c03129f..805e5bf 100644 --- a/resources/syncplayintf.lua +++ b/resources/syncplayintf.lua @@ -6,19 +6,17 @@ local CANVAS_WIDTH = 1920 local CANVAS_HEIGHT = 1080 local ROW_HEIGHT = 100 local PIXELS_PER_CHAR = 25 -local CHAT_FORMAT = "{\\fs15}{\an1}{\\q2}" +local CHAT_FORMAT = "{\\fs50}{\an1}{\\q2}" local MAX_ROWS = 7 local MOVEMENT_PER_SECOND = 200 local TICK_INTERVAL = 0.01 -local INPUT_PROMPT_FONT_SIZE = 20 -local MAX_CHAT_MESSAGE_LENGTH = 50 local chat_log = {} local assdraw = require "mp.assdraw" function format_chat(xpos, ypos, text) - chat_message = CHAT_FORMAT .. "{\\fs50}{\\pos("..xpos..","..ypos..")}"..text.."\n" + chat_message = CHAT_FORMAT .. "{\\pos("..xpos..","..ypos..")}"..text.."\n" return string.format(chat_message) end @@ -26,6 +24,7 @@ function clear_chat() chat_log = {} end + function add_chat(chat_message) local entry = #chat_log+1 for i = 1, #chat_log do @@ -69,6 +68,10 @@ mp.register_script_message('chat', function(e) add_chat(e) end) +mp.register_script_message('set_syncplayintf_options', function(e) + set_syncplayintf_options(e) +end) + -- adapted from repl.lua -- A graphical REPL for mpv input commands -- -- c 2016, James Ross-Gowan @@ -94,10 +97,15 @@ local opts = { scale = 1, -- Set the font used for the REPL and the console. This probably doesn't -- have to be a monospaced font. - font = 'monospace', + ['chatInputFontFamily'] = 'monospace', -- Set the font size used for the REPL and the console. This will be -- multiplied by "scale." - ['font-size'] = INPUT_PROMPT_FONT_SIZE, + ['chatInputFontSize'] = 20, + ['chatInputFontWeight'] = 1, + ['chatInputFontUnderline'] = false, + ['chatInputFontColor'] = "#000000", + ['chatInputPosition'] = "Top", + ['MaxChatMessageLength'] = 50, } function detect_platform() @@ -149,30 +157,59 @@ function input_ass() if not repl_active then return "" end - + local bold + if opts['chatInputFontWeight'] < 75 then + bold = 0 + else + bold = 1 + end + local underline = opts['chatInputFontUnderline'] and 1 or 0 + local red = string.sub(opts['chatInputFontColor'],2,3) + local green = string.sub(opts['chatInputFontColor'],4,5) + local blue = string.sub(opts['chatInputFontColor'],6,7) + local fontColor = blue .. green .. red local style = '{\\r' .. '\\1a&H00&\\3a&H00&\\4a&H99&' .. - '\\1c&Heeeeee&\\3c&H111111&\\4c&H000000&' .. - '\\fn' .. opts.font .. '\\fs' .. opts['font-size'] .. + '\\1c&H'..fontColor..'&\\3c&H111111&\\4c&H000000&' .. + '\\fn' .. opts['chatInputFontFamily'] .. '\\fs' .. opts['chatInputFontSize'] .. '\\b' .. bold .. '\\bord2\\xshad0\\yshad1\\fsp0\\q1}' + + local after_style = '{\\u' .. underline .. '}' -- Create the cursor glyph as an ASS drawing. ASS will draw the cursor -- inline with the surrounding text, but it sets the advance to the width -- of the drawing. So the cursor doesn't affect layout too much, make it as -- thin as possible and make it appear to be 1px wide by giving it 0.5px -- horizontal borders. - local cheight = opts['font-size'] * 8 + local cheight = opts['chatInputFontSize'] * 8 local cglyph = '{\\r' .. '\\1a&H44&\\3a&H44&\\4a&H99&' .. - '\\1c&Heeeeee&\\3c&Heeeeee&\\4c&H000000&' .. + '\\1c&H'..fontColor..'&\\3c&Heeeeee&\\4c&H000000&' .. '\\xbord0.5\\ybord0\\xshad0\\yshad1\\p4\\pbo24}' .. 'm 0 0 l 1 0 l 1 ' .. cheight .. ' l 0 ' .. cheight .. '{\\p0}' local before_cur = ass_escape(line:sub(1, cursor - 1)) local after_cur = ass_escape(line:sub(cursor)) - --mp.osd_message("",0) - return "{\\an7}{\\pos(5,5)}"..style..'> '..before_cur..cglyph..style..after_cur + local alignment = 7 + local position = "5,5" + local end_marker = "" + if opts['chatInputPosition'] == "Middle" then + alignment = 5 + position = tostring(CANVAS_WIDTH/2)..","..tostring(CANVAS_HEIGHT/2) + end_marker = "{\\u0}".." <" + elseif opts['chatInputPosition'] == "Bottom" then + alignment = 1 + position = tostring(5)..","..tostring(CANVAS_HEIGHT-5) + end + --mp.osd_message("",0) + return "{\\an"..alignment.."}{\\pos("..position..")}"..style..'> '..after_style..before_cur..cglyph..style..after_style..after_cur..end_marker + +end + +function escape() + set_active(false) + clear() end -- Set the REPL visibility (`, Esc) @@ -227,7 +264,25 @@ function prev_utf8(str, pos) end function trim_input() - -- TODO + -- Naive helper function to find the next UTF-8 character in 'str' after 'pos' +-- by skipping continuation bytes. Assumes 'str' contains valid UTF-8. + local str = line + if str == nil or str == "" or str:len() <= opts['MaxChatMessageLength'] then + return + end + local pos = 0 + local oldPos = -1 + local chars = 0 + + repeat + oldPos = pos + pos = next_utf8(str, pos) + chars = chars + 1 + until pos == oldPos or chars > opts['MaxChatMessageLength'] + line = line:sub(1,pos-1) + if cursor > pos then + cursor = pos + end return end @@ -412,7 +467,7 @@ local binding_name_map = { -- List of input bindings. This is a weird mashup between common GUI text-input -- bindings and readline bindings. local bindings = { - { 'esc', function() set_active(false) end }, + { 'esc', function() escape() end }, { 'bs', handle_backspace }, { 'shift+bs', handle_backspace }, { 'del', handle_del }, @@ -449,4 +504,23 @@ mp.register_script_message('type', function(text) end) mp.add_key_binding('enter', handle_enter) -mp.add_key_binding('kp_enter', handle_enter) \ No newline at end of file +mp.add_key_binding('kp_enter', handle_enter) +mp.command('print-text ""') + +function set_syncplayintf_options(input) + ---mp.command('print-text "...'..input..'"') + for option, value in string.gmatch(input, "([^ ,=]+)=([^[,]+)") do + local valueType = type(opts[option]) + if valueType == "number" then + value = tonumber(value) + elseif valueType == "boolean" then + if value == "True" then + value = true + else + value = false + end + end + opts[option] = value + ---mp.command('print-text "'..option.."="..tostring(value).." - "..valueType..'"') + end +end \ No newline at end of file diff --git a/resources/user_comment.png b/resources/user_comment.png new file mode 100644 index 0000000000000000000000000000000000000000..e54ebebafb5072fabac9a0f3d8a79fcee3265f9f GIT binary patch literal 743 zcmV?P)vCi#|P&Xm-dkucwL z3)87{8iWe96huvPHfK`KOdC2Z({T6vJ9pwDx$D4>d(Pqff6w7Lmj{5i6;ZyPPpPN; zroaW=6d#@oL2Fa53F~$Su10(RG%K0p3VTuP3?Z=nBA8z$uq+XLUL^QrC74`bU|!e| zr>hK{)%Q!vdmIO5Z3JIvaOyjOX`X@c8-ua03`Q&)f&%p*{(A$q`ZTTjk%q_T7>v^J zu!R-a9fFLScYlKkNBP_Cob=9m9JLVoC-?c{)eOtMnh7qNN{ejy2sM{pS^mgFHJm@(buuM4>=<5Vr$&Kzw{B?uPr; z(1Yf=#g)zADkWnx=MR%ykl| z3Ui42k+O2{bCn)01-s5Sxp|z{G2di&KT(_M6;$EI zDL57JFf}cw4bP1P$pgTRKH$0@h|~aA>j`qZ2*kU5t2EVD5#~@VNhqx{vz8ethDD-=+1vnemftUBA zF;N!Q%PBB5B=KLB#QO(CHe?;R+-C8M?ppDW>R$5`cCPq@YpusFRTaH1i9Kv;l<>I( Ze*oTy+;kdDB`N>_002ovPDHLkV1l3CM+g7_ literal 0 HcmV?d00001 diff --git a/syncplay/constants.py b/syncplay/constants.py index 69a1123..d9e0e57 100644 --- a/syncplay/constants.py +++ b/syncplay/constants.py @@ -55,6 +55,12 @@ SYNC_ON_PAUSE = True # Client seek to global position - subtitles may disappear PLAYLIST_MAX_CHARACTERS = 10000 PLAYLIST_MAX_ITEMS = 250 MAXIMUM_TAB_WIDTH = 350 +DEFAULT_WINDOWS_MONOSPACE_FONT = "Consolas" +DEFAULT_OSX_MONOSPACE_FONT = "Menlo" +FALLBACK_MONOSPACE_FONT = "Monospace" +DEFAULT_CHAT_INPUT_FONT_SIZE = 18 +DEFAULT_CHAT_INPUT_FONT_COLOR = "#FFFFFF" +DEFAULT_CHAT_INPUT_FONT_WEIGHT = 1 # Maximum character lengths (for client and server) MAX_CHAT_MESSAGE_LENGTH = 50 # Number of displayed characters @@ -170,6 +176,8 @@ MPV_ARGS = ['--force-window', '--idle', '--hr-seek=always', '--keep-open'] MPV_SLAVE_ARGS = ['--msg-level=all=error,cplayer=info,term-msg=info', '--input-terminal=no', '--input-file=/dev/stdin'] MPV_SLAVE_ARGS_NEW = ['--term-playing-msg=\nANS_filename=${filename}\nANS_length=${=length:${=duration:0}}\nANS_path=${path}\n', '--terminal=yes'] MPV_NEW_VERSION = False +MPV_SYNCPLAYINTF_OPTIONS_TO_SEND = ["chatInputFontFamily", "chatInputFontSize", "chatInputFontWeight", "chatInputFontUnderline", "chatInputFontColor", "chatInputPosition"] +MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND = ["MaxChatMessageLength={}".format(MAX_CHAT_MESSAGE_LENGTH)] VLC_SLAVE_ARGS = ['--extraintf=luaintf', '--lua-intf=syncplay', '--no-quiet', '--no-input-fast-seek', '--play-and-pause', '--start-time=0'] VLC_SLAVE_NONOSX_ARGS = ['--no-one-instance', '--no-one-instance-when-started-from-file'] @@ -190,6 +198,9 @@ UNPAUSE_IFALREADYREADY_MODE = "IfAlreadyReady" UNPAUSE_IFOTHERSREADY_MODE = "IfOthersReady" UNPAUSE_IFMINUSERSREADY_MODE = "IfMinUsersReady" UNPAUSE_ALWAYS_MODE = "Always" +INPUT_POSITION_TOP = "Top" +INPUT_POSITION_MIDDLE = "Middle" +INPUT_POSITION_BOTTOM = "Bottom" PRIVACY_HIDDENFILENAME = "**Hidden filename**" INVERTED_STATE_MARKER = "*" diff --git a/syncplay/messages.py b/syncplay/messages.py index a62326d..33bb632 100755 --- a/syncplay/messages.py +++ b/syncplay/messages.py @@ -67,3 +67,4 @@ def getMessage(type_, locale=None): return unicode(messages["en"][type_]) else: raise KeyError(type_) + #print u"WARNING: Cannot find message '{}'!".format(type_) \ No newline at end of file diff --git a/syncplay/messages_de.py b/syncplay/messages_de.py index e10c93b..b90b5a0 100644 --- a/syncplay/messages_de.py +++ b/syncplay/messages_de.py @@ -230,6 +230,7 @@ de = { "messages-label" : u"Nachrichten", "messages-osd-title" : u"OSD-(OnScreenDisplay)-Einstellungen", "messages-other-title" : u"Weitere Display-Einstellungen", + "chat-label" : u"Chat", # TODO: Translate "privacy-label" : u"Privatsphäre", "privacy-title" : u"Privatsphäreneinstellungen", "unpause-title" : u"Wenn du Play drückst, auf Bereit setzen und:", @@ -239,6 +240,16 @@ de = { "unpause-always" : u"Immer wiedergeben", "syncplay-trusteddomains-title": u"Trusted domains (for streaming services and hosted content)", # TODO: Translate into German + "chat-title": u"Chat message input", # TODO: Translate + "chatinputenabled-label": u"Enable chat input via mpv (using enter key)", # TODO: Translate + "chatinputfont-label": u"Chat input font", # TODO: Translate + "chatfont-label": u"Set font", # TODO: Translate + "chatcolour-label": u"Set colour", # TODO: Translate + "chatinputposition-label": u"Position of message input area in mpv", # TODO: Translate + "chat-top-option": u"Top", # TODO: Translate + "chat-middle-option": u"Middle", # TODO: Translate + "chat-bottom-option": u"Bottom", # TODO: Translate + "help-label" : u"Hilfe", "reset-label" : u"Standardwerte zurücksetzen", "run-label" : u"Syncplay starten", @@ -354,6 +365,15 @@ de = { "unpause-ifminusersready-tooltip" : u"Wenn du Play drückst und nicht bereit bist, wird nur gestartet, wenn die minimale Anzahl anderer Benutzer bereit ist.", "trusteddomains-arguments-tooltip" : u"Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.", # TODO: Translate into German + "chatinputenabled-tooltip": u"Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)", # TODO: Translate + "font-label-tooltip": u"Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate + "set-input-font-tooltip": u"Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate + "set-input-colour-tooltip": u"Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", # TODO: Translate + "chatinputposition-tooltip": u"Location in mpv where chat input text will appear when you press enter and type.", # TODO: Translate + "chatinputposition-top-tooltip": u"Place chat input at top of mpv window.", # TODO: Translate + "chatinputposition-middle-tooltip": u"Place chat input in dead centre of mpv window.", # TODO: Translate + "chatinputposition-bottom-tooltip": u"Place chat input at bottom of mpv window.", # TODO: Translate + "help-tooltip" : u"Öffnet Hilfe auf syncplay.pl [Englisch]", "reset-tooltip" : u"Alle Einstellungen auf Standardwerte zurücksetzen.", "update-server-list-tooltip" : u"Mit syncplay.pl verbinden um die Liste öffentlicher Server zu aktualisieren.", diff --git a/syncplay/messages_en.py b/syncplay/messages_en.py index 3883185..cc23e60 100644 --- a/syncplay/messages_en.py +++ b/syncplay/messages_en.py @@ -229,6 +229,7 @@ en = { "messages-label" : "Messages", "messages-osd-title" : "On-screen Display settings", "messages-other-title" : "Other display settings", + "chat-label" : u"Chat", "privacy-label" : "Privacy", # Currently unused, but will be brought back if more space is needed in Misc tab "privacy-title" : "Privacy settings", "unpause-title" : u"If you press play, set as ready and:", @@ -237,6 +238,16 @@ en = { "unpause-ifminusersready-option" : u"Unpause if already ready or if all others ready and min users ready", "unpause-always" : u"Always unpause", "syncplay-trusteddomains-title": u"Trusted domains (for streaming services and hosted content)", + + "chat-title" : u"Chat message input", + "chatinputenabled-label" : u"Enable chat input via mpv (using enter key)", + "chatinputfont-label" : u"Chat input font", + "chatfont-label" : u"Set font", + "chatcolour-label" : u"Set colour", + "chatinputposition-label" : u"Position of message input area in mpv", + "chat-top-option" : u"Top", + "chat-middle-option" : u"Middle", + "chat-bottom-option" : u"Bottom", "help-label" : "Help", "reset-label" : "Restore defaults", @@ -351,6 +362,15 @@ en = { "unpause-ifminusersready-tooltip" : u"If you press unpause when not ready, it will only unpause if others are ready and minimum users threshold is met.", "trusteddomains-arguments-tooltip" : u"Domains that it is okay for Syncplay to automatically switch to when shared playlists is enabled.", + "chatinputenabled-tooltip" : u"Enable chat input in mpv (press enter to chat, enter to send, escape to cancel)", + "font-label-tooltip" : u"Font used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", + "set-input-font-tooltip" : u"Font family used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", + "set-input-colour-tooltip" : u"Font colour used for when entering chat messages in mpv. Client-side only, so doesn't affect what other see.", + "chatinputposition-tooltip" : u"Location in mpv where chat input text will appear when you press enter and type.", + "chatinputposition-top-tooltip" : u"Place chat input at top of mpv window.", + "chatinputposition-middle-tooltip" : u"Place chat input in dead centre of mpv window.", + "chatinputposition-bottom-tooltip" : u"Place chat input at bottom of mpv window.", + "help-tooltip" : "Opens the Syncplay.pl user guide.", "reset-tooltip" : "Reset all settings to the default configuration.", "update-server-list-tooltip" : u"Connect to syncplay.pl to update list of public servers.", diff --git a/syncplay/messages_ru.py b/syncplay/messages_ru.py index 33db39c..2b5a511 100644 --- a/syncplay/messages_ru.py +++ b/syncplay/messages_ru.py @@ -232,6 +232,7 @@ ru = { "messages-label" : u"Сообщения", "messages-osd-title" : u"Настройки OSD", "messages-other-title" : u"Другие настройки отображения", + "chat-label" : u"Chat", # TODO: Translate "privacy-label" : u"Приватность", "privacy-title" : u"Настройки приватности", "unpause-title" : u"Если вы стартуете, то:", @@ -242,6 +243,16 @@ ru = { "syncplay-trusteddomains-title": u"Доверенные сайты (стрим-сервисы, видеохостинги, файлы в сети)", "addtrusteddomain-menu-label" : u"Добавить {} как доверенный сайт", # Domain + "chat-title": u"Chat message input", # TODO: Translate + "chatinputenabled-label": u"Enable chat input via mpv (using enter key)", # TODO: Translate + "chatinputfont-label": u"Chat input font", # TODO: Translate + "chatfont-label": u"Set font", # TODO: Translate + "chatcolour-label": u"Set colour", # TODO: Translate + "chatinputposition-label": u"Position of message input area in mpv", # TODO: Translate + "chat-top-option": u"Top", # TODO: Translate + "chat-middle-option": u"Middle", # TODO: Translate + "chat-bottom-option": u"Bottom", # TODO: Translate + "help-label" : u"Помощь", "reset-label" : u"Сброс настроек", "run-label" : u"Запустить", diff --git a/syncplay/players/mpv.py b/syncplay/players/mpv.py index bae78d0..6c75ae8 100644 --- a/syncplay/players/mpv.py +++ b/syncplay/players/mpv.py @@ -230,6 +230,15 @@ class NewMpvPlayer(OldMpvPlayer): if "" in line: self._listener.sendChat(line[6:-7]) + if "" in line: + options = [] + for option in constants.MPV_SYNCPLAYINTF_OPTIONS_TO_SEND: + options.append(u"{}={}".format(option,self._client._config[option])) + for option in constants.MPV_SYNCPLAYINTF_CONSTANTS_TO_SEND: + options.append(option) + options_string = ", ".join(options) + self._listener.sendLine(u'script-message-to syncplayintf set_syncplayintf_options "{}"'.format(options_string)) + if line == "" or "Playing:" in line: self._listener.setReadyToSend(False) self._clearFileLoaded() diff --git a/syncplay/ui/ConfigurationGetter.py b/syncplay/ui/ConfigurationGetter.py index 586933e..615e94a 100755 --- a/syncplay/ui/ConfigurationGetter.py +++ b/syncplay/ui/ConfigurationGetter.py @@ -7,6 +7,7 @@ from syncplay import constants, utils, version, milestone from syncplay.messages import getMessage, setLanguage, isValidLanguage from syncplay.players.playerFactory import PlayerFactory import codecs +import re class InvalidConfigValue(Exception): def __init__(self, message): @@ -62,7 +63,14 @@ class ConfigurationGetter(object): "showSameRoomOSD" : True, "showNonControllerOSD" : False, "showContactInfo" : True, - "showDurationNotification" : True + "showDurationNotification" : True, + "chatInputEnabled" : True, + "chatInputFontFamily" : utils.getDefaultMonospaceFont(), + "chatInputFontSize" : constants.DEFAULT_CHAT_INPUT_FONT_SIZE, + "chatInputFontWeight" : constants.DEFAULT_CHAT_INPUT_FONT_WEIGHT, + "chatInputFontUnderline": False, + "chatInputFontColor": constants.DEFAULT_CHAT_INPUT_FONT_COLOR, + "chatInputPosition": constants.INPUT_POSITION_TOP } self._defaultConfig = self._config.copy() @@ -104,7 +112,9 @@ class ConfigurationGetter(object): "sharedPlaylistEnabled", "loopAtEndOfPlaylist", "loopSingleFiles", - "onlySwitchToTrustedDomains" + "onlySwitchToTrustedDomains", + "chatInputEnabled", + "chatInputFontUnderline", ] self._tristate = [ "checkForUpdatesAutomatically", @@ -122,6 +132,12 @@ class ConfigurationGetter(object): "rewindThreshold", "fastforwardThreshold", "autoplayMinUsers", + "chatInputFontSize", + "chatInputFontWeight" + ] + + self._hexadecimal = [ + "chatInputFontColor" ] self._iniStructure = { @@ -140,7 +156,11 @@ class ConfigurationGetter(object): "onlySwitchToTrustedDomains", "trustedDomains"], "gui": ["showOSD", "showOSDWarnings", "showSlowdownOSD", "showDifferentRoomOSD", "showSameRoomOSD", - "showNonControllerOSD", "showDurationNotification"], + "showNonControllerOSD", "showDurationNotification", + "chatInputEnabled","chatInputFontUnderline", + "chatInputFontFamily", "chatInputFontSize", + "chatInputFontWeight", "chatInputFontColor", + "chatInputPosition"], "general": ["language", "checkForUpdatesAutomatically", "lastCheckedForUpdates"] } @@ -194,6 +214,11 @@ class ConfigurationGetter(object): for key in self._numeric: self._config[key] = float(self._config[key]) + for key in self._hexadecimal: + match = re.search(r'^#(?:[0-9a-fA-F]){6}$', self._config[key]) + if not match: + self._config[key] = u"#FFFFFF" + for key in self._required: if key == "playerPath": player = None diff --git a/syncplay/ui/GuiConfiguration.py b/syncplay/ui/GuiConfiguration.py index 089a018..1b33a10 100644 --- a/syncplay/ui/GuiConfiguration.py +++ b/syncplay/ui/GuiConfiguration.py @@ -476,7 +476,7 @@ class ConfigDialog(QtGui.QDialog): def connectChildren(self, widget): widgetName = str(widget.objectName()) - if self.subitems.has_key(widgetName) and isinstance(widget, QCheckBox): + if self.subitems.has_key(widgetName): widget.stateChanged.connect(lambda: self.updateSubwidgets(self, widget)) self.updateSubwidgets(self, widget) @@ -823,6 +823,94 @@ class ConfigDialog(QtGui.QDialog): self.syncSettingsLayout.setAlignment(Qt.AlignTop) self.stackedLayout.addWidget(self.syncSettingsFrame) + def addChatTab(self): + self.chatFrame = QtGui.QFrame() + self.chatLayout = QtGui.QVBoxLayout() + self.chatLayout.setAlignment(Qt.AlignTop) + + # Input + self.chatInputGroup = QtGui.QGroupBox(getMessage("chat-title")) + self.chatInputLayout = QtGui.QGridLayout() + self.chatLayout.addWidget(self.chatInputGroup) + self.chatInputGroup.setLayout(self.chatInputLayout) + self.chatInputEnabledCheckbox = QCheckBox(getMessage("chatinputenabled-label")) + self.chatInputEnabledCheckbox.setObjectName("chatInputEnabled") + self.chatInputLayout.addWidget(self.chatInputEnabledCheckbox, 1, 0, 1,1, Qt.AlignLeft) + + self.inputFontLayout = QtGui.QHBoxLayout() + self.inputFontLayout.setContentsMargins(0, 0, 0, 0) + self.inputFontFrame = QtGui.QFrame() + self.inputFontFrame.setLayout(self.inputFontLayout) + self.inputFontFrame.setSizePolicy(QtGui.QSizePolicy.Minimum, QtGui.QSizePolicy.Minimum) + self.chatFontLabel = QLabel(getMessage("chatinputfont-label"), self) + self.chatFontLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png")) + self.chatFontLabel.setObjectName("font-label") + self.chatInputFontButton = QtGui.QPushButton(getMessage("chatfont-label")) + self.chatInputFontButton.setObjectName("set-input-font") + self.chatInputFontButtonGroup = QtGui.QButtonGroup() + self.chatInputFontButtonGroup.addButton(self.chatInputFontButton) + self.chatInputFontButton.released.connect(lambda: self.fontDialog("chatInput")) + self.chatInputColourButton = QtGui.QPushButton(getMessage("chatcolour-label")) + self.chatInputColourButton.setObjectName("set-input-colour") + self.chatInputColourButtonGroup = QtGui.QButtonGroup() + self.chatInputColourButtonGroup.addButton(self.chatInputColourButton) + self.chatInputColourButton.released.connect(lambda: self.colourDialog("chatInput")) + self.inputFontLayout.addWidget(self.chatFontLabel, Qt.AlignLeft) + self.inputFontLayout.addWidget(self.chatInputFontButton, Qt.AlignLeft) + self.inputFontLayout.addWidget(self.chatInputColourButton, Qt.AlignLeft) + self.chatInputLayout.addWidget(self.inputFontFrame, 2, 0, 1, 3, Qt.AlignLeft) + + self.chatInputPositionLabel = QLabel(getMessage("chatinputposition-label"), self) + self.chatInputPositionLabel.setStyleSheet(constants.STYLE_SUBCHECKBOX.format(self.posixresourcespath + u"chevrons_right.png")) + self.chatInputPositionGroup = QButtonGroup() + self.chatInputTopOption = QRadioButton(getMessage("chat-top-option")) + self.chatInputMiddleOption = QRadioButton(getMessage("chat-middle-option")) + self.chatInputBottomOption = QRadioButton(getMessage("chat-bottom-option")) + self.chatInputPositionGroup.addButton(self.chatInputTopOption) + self.chatInputPositionGroup.addButton(self.chatInputMiddleOption) + self.chatInputPositionGroup.addButton(self.chatInputBottomOption) + + self.chatInputPositionLabel.setObjectName("chatinputposition") + self.chatInputTopOption.setObjectName("chatinputposition-top" + constants.CONFIG_NAME_MARKER + "chatInputPosition" + constants.CONFIG_VALUE_MARKER + constants.INPUT_POSITION_TOP) + self.chatInputMiddleOption.setObjectName("chatinputposition-middle" + constants.CONFIG_NAME_MARKER + "chatInputPosition" + constants.CONFIG_VALUE_MARKER + constants.INPUT_POSITION_MIDDLE) + self.chatInputBottomOption.setObjectName("chatinputposition-bottom" + constants.CONFIG_NAME_MARKER + "chatInputPosition" + constants.CONFIG_VALUE_MARKER + constants.INPUT_POSITION_BOTTOM) + + self.chatInputLayout.addWidget(self.chatInputPositionLabel, 3, 0) + self.chatInputLayout.addWidget(self.chatInputTopOption, 3, 1, Qt.AlignLeft) + self.chatInputLayout.addWidget(self.chatInputMiddleOption, 3, 2, Qt.AlignLeft) + self.chatInputLayout.addWidget(self.chatInputBottomOption, 3, 3, Qt.AlignLeft) + + self.subitems['chatInputEnabled'] = [self.chatInputPositionLabel.objectName(), self.chatInputTopOption.objectName(), + self.chatInputMiddleOption.objectName(), self.chatInputBottomOption.objectName(), + self.chatInputFontButton.objectName(), self.chatFontLabel.objectName(), + self.chatInputColourButton.objectName()] + + + + # chatFrame + self.chatFrame.setLayout(self.chatLayout) + self.stackedLayout.addWidget(self.chatFrame) + + def fontDialog(self, configName): + font = QtGui.QFont() + font.setFamily(self.config[configName+ u"FontFamily"]) + font.setPointSize(self.config[configName + u"FontSize"]) + font.setWeight(self.config[configName + u"FontWeight"]) + font.setUnderline(self.config[configName + u"FontUnderline"]) + value, ok = QtGui.QFontDialog.getFont(font) + if ok: + self.config[configName + u"FontFamily"] = value.family() + self.config[configName + u"FontSize"] = value.pointSize() + self.config[configName + u"FontWeight"] = value.weight() + self.config[configName + u"FontUnderline"] = value.underline() + + def colourDialog(self, configName): + oldColour = QtGui.QColor() + oldColour.setNamedColor(self.config[configName+ u"FontColor"]) + colour = QtGui.QColorDialog.getColor(oldColour, self) + if colour.isValid(): + self.config[configName + u"FontColor"] = colour.name() + def addMessageTab(self): self.messageFrame = QtGui.QFrame() self.messageLayout = QtGui.QVBoxLayout() @@ -922,18 +1010,18 @@ class ConfigDialog(QtGui.QDialog): self.helpButton = QtGui.QPushButton(QtGui.QIcon(self.resourcespath + u'help.png'), getMessage("help-label")) self.helpButton.setObjectName("help") self.helpButton.setMaximumSize(self.helpButton.sizeHint()) - self.helpButton.pressed.connect(self.openHelp) + self.helpButton.released.connect(self.openHelp) self.resetButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + u'cog_delete.png'),getMessage("reset-label")) self.resetButton.setMaximumSize(self.resetButton.sizeHint()) self.resetButton.setObjectName("reset") - self.resetButton.pressed.connect(self.resetSettings) + self.resetButton.released.connect(self.resetSettings) self.runButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("run-label")) - self.runButton.pressed.connect(self._runWithoutStoringConfig) + self.runButton.released.connect(self._runWithoutStoringConfig) self.runButton.setToolTip(getMessage("nostore-tooltip")) self.storeAndRunButton = QtGui.QPushButton(QtGui.QIcon(resourcespath + u'accept.png'), getMessage("storeandrun-label")) - self.storeAndRunButton.pressed.connect(self._saveDataAndLeave) + self.storeAndRunButton.released.connect(self._saveDataAndLeave) self.bottomButtonLayout.addWidget(self.helpButton) self.bottomButtonLayout.addWidget(self.resetButton) self.bottomButtonLayout.addWidget(self.runButton) @@ -963,7 +1051,8 @@ class ConfigDialog(QtGui.QDialog): self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"house.png"),getMessage("basics-label"))) self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"control_pause_blue.png"),getMessage("readiness-label"))) self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"film_link.png"),getMessage("sync-label"))) - self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"comments.png"),getMessage("messages-label"))) + self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"user_comment.png"), getMessage("chat-label"))) + self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"error.png"),getMessage("messages-label"))) self.tabListWidget.addItem(QtGui.QListWidgetItem(QtGui.QIcon(self.resourcespath + u"cog.png"),getMessage("misc-label"))) self.tabListLayout.addWidget(self.tabListWidget) self.tabListFrame.setLayout(self.tabListLayout) @@ -1070,6 +1159,7 @@ class ConfigDialog(QtGui.QDialog): self.addBasicTab() self.addReadinessTab() self.addSyncTab() + self.addChatTab() self.addMessageTab() self.addMiscTab() self.tabList() diff --git a/syncplay/utils.py b/syncplay/utils.py index 4ce630f..646e977 100644 --- a/syncplay/utils.py +++ b/syncplay/utils.py @@ -135,6 +135,14 @@ def findWorkingDir(): path = "" return path +def getDefaultMonospaceFont(): + if platform.system() == "Windows": + return constants.DEFAULT_WINDOWS_MONOSPACE_FONT + elif platform.system() == "Darwin": + return constants.DEFAULT_OSX_MONOSPACE_FONT + else: + return constants.FALLBACK_MONOSPACE_FONT + def limitedPowerset(s, minLength): return itertools.chain.from_iterable(itertools.combinations(s, r) for r in xrange(len(s), minLength, -1))