From 1cacde34f9b6a0816ea94b76c989c3e61500eeb6 Mon Sep 17 00:00:00 2001 From: ingvar1995 Date: Wed, 27 Apr 2016 21:10:53 +0300 Subject: [PATCH] typing notifications full support --- README.md | 13 ++++++++----- src/callbacks.py | 5 +++++ src/images/typing.png | Bin 0 -> 5777 bytes src/mainscreen.py | 13 +++++++++++++ src/profile.py | 16 ++++++++++++++++ src/settings.py | 2 +- 6 files changed, 43 insertions(+), 6 deletions(-) create mode 100755 src/images/typing.png diff --git a/README.md b/README.md index 659bd3e..090cc2a 100644 --- a/README.md +++ b/README.md @@ -2,29 +2,32 @@ Toxygen is simple [Tox](https://tox.chat/) client written on pure Python ### [How to install](/docs/install.md) +### [Contributing](/docs/contributing.md) + # Supported OS: - Windows - Linux ###Features - [x] 1v1 messages -- [x] Contact aliases +- [x] File transfers +- [x] Audio - [x] Chat history - [x] Name lookups (TOX DNS 4 support) - [x] Profile import/export +- [x] Inline images - [x] Message splitting - [x] Proxy support -- [x] File transfers - [x] Avatars - [x] Multiprofile - [x] Multilingual - [x] Sound notifications -- [x] Inline images -- [x] Audio +- [x] Contact aliases +- [x] Contact blocking +- [x] Typing notifications - [ ] Video - [ ] Save file encryption - [ ] File resuming -- [ ] Typing notifications - [ ] Changing nospam - [ ] Plugins support - [ ] Group chats diff --git a/src/callbacks.py b/src/callbacks.py index 5e813ba..7e97137 100644 --- a/src/callbacks.py +++ b/src/callbacks.py @@ -137,6 +137,10 @@ def friend_request(tox, public_key, message, message_size, user_data): if tox_id not in Settings.get_instance()['blocked']: invoke_in_main_thread(profile.process_friend_request, tox_id, message.decode('utf-8')) + +def friend_typing(tox, friend_number, typing, user_data): + invoke_in_main_thread(Profile.get_instance().friend_typing, friend_number, typing) + # ----------------------------------------------------------------------------------------------------------------- # Callbacks - file transfers # ----------------------------------------------------------------------------------------------------------------- @@ -253,6 +257,7 @@ def init_callbacks(tox, window, tray): tox.callback_friend_name(friend_name, 0) tox.callback_friend_status_message(friend_status_message, 0) tox.callback_friend_request(friend_request, 0) + tox.callback_friend_typing(friend_typing, 0) tox.callback_file_recv(tox_file_recv(window, tray), 0) tox.callback_file_recv_chunk(file_recv_chunk, 0) diff --git a/src/images/typing.png b/src/images/typing.png new file mode 100755 index 0000000000000000000000000000000000000000..26ad69b74d4036f93b0ac78936cb81f366a50e49 GIT binary patch literal 5777 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 literal 0 HcmV?d00001 diff --git a/src/mainscreen.py b/src/mainscreen.py index 0dc8b9c..9145bce 100644 --- a/src/mainscreen.py +++ b/src/mainscreen.py @@ -10,6 +10,8 @@ class MessageArea(QtGui.QPlainTextEdit): def __init__(self, parent, form): super(MessageArea, self).__init__(parent) self.parent = form + self.timer = QtCore.QTimer(self) + self.timer.timeout.connect(lambda: self.parent.profile.send_typing(False)) def keyPressEvent(self, event): if event.key() == QtCore.Qt.Key_Return: @@ -21,6 +23,10 @@ class MessageArea(QtGui.QPlainTextEdit): elif event.key() == QtCore.Qt.Key_Up and not self.toPlainText(): self.appendPlainText(Profile.get_instance().get_last_message()) else: + self.parent.profile.send_typing(True) + if self.timer.isActive(): + self.timer.stop() + self.timer.start(5000) super(MessageArea, self).keyPressEvent(event) @@ -208,6 +214,13 @@ class MainWindow(QtGui.QMainWindow): self.callButton.setObjectName("callButton") self.callButton.clicked.connect(self.call) self.update_call_state('call') + self.typing = QtGui.QLabel(Form) + self.typing.setGeometry(QtCore.QRect(500, 40, 50, 30)) + pixmap = QtGui.QPixmap(QtCore.QSize(50, 30)) + pixmap.load(curr_directory() + '/images/typing.png') + self.typing.setScaledContents(False) + self.typing.setPixmap(pixmap.scaled(50, 30, QtCore.Qt.KeepAspectRatio)) + self.typing.setVisible(False) QtCore.QMetaObject.connectSlotsByName(Form) def setup_left_center(self, widget): diff --git a/src/profile.py b/src/profile.py index 4dd0eb7..488f054 100644 --- a/src/profile.py +++ b/src/profile.py @@ -394,6 +394,8 @@ class Profile(Contact, Singleton): self._screen.messageEdit.clear() return try: + self.send_typing(False) + self._screen.typing.setVisible(False) if value is not None: self._active_friend = value friend = self._friends[value] @@ -454,6 +456,20 @@ class Profile(Contact, Singleton): def is_active_online(self): return self._active_friend + 1 and self._friends[self._active_friend].status is not None + # ----------------------------------------------------------------------------------------------------------------- + # Typing notifications + # ----------------------------------------------------------------------------------------------------------------- + + def send_typing(self, typing): + if Settings.get_instance()['typing_notifications']: + friend = self._friends[self._active_friend] + if friend.status is not None: + self._tox.self_set_typing(friend.number, typing) + + def friend_typing(self, friend_number, typing): + if friend_number == self.get_active_number(): + self._screen.typing.setVisible(typing) + # ----------------------------------------------------------------------------------------------------------------- # Private messages # ----------------------------------------------------------------------------------------------------------------- diff --git a/src/settings.py b/src/settings.py index e1b6986..8de65c9 100644 --- a/src/settings.py +++ b/src/settings.py @@ -62,7 +62,7 @@ class Settings(Singleton, dict): 'show_online_friends': False, 'auto_accept_from_friends': [], 'friends_aliases': [], - 'typing_notifications': True, + 'typing_notifications': False, 'calls_sound': True, 'blocked': [] }