Implement paused chate state. fixed #2124
This commit is contained in:
parent
35b6e146cb
commit
a6c56682b7
4 changed files with 69 additions and 4 deletions
|
@ -629,7 +629,6 @@ class Core(object):
|
|||
"""
|
||||
res = read_char(self.stdscr)
|
||||
while res is None:
|
||||
log.debug('checking events')
|
||||
self.check_timed_events()
|
||||
res = read_char(self.stdscr)
|
||||
return res
|
||||
|
@ -707,6 +706,7 @@ class Core(object):
|
|||
if not options.debug:
|
||||
curses.raw()
|
||||
theme.init_colors()
|
||||
stdscr.idlok(True)
|
||||
stdscr.keypad(True)
|
||||
curses.ungetch(" ") # H4X: without this, the screen is
|
||||
stdscr.getkey() # erased on the first "getkey()"
|
||||
|
|
|
@ -51,7 +51,7 @@ def read_char(s):
|
|||
# last_char_time = time.time()
|
||||
s.timeout(1000)
|
||||
(first, char) = get_next_byte(s)
|
||||
if first is None:
|
||||
if first is None and char is None:
|
||||
return None
|
||||
if not isinstance(first, int): # Keyboard special, like KEY_HOME etc
|
||||
return char
|
||||
|
|
54
src/tabs.py
54
src/tabs.py
|
@ -40,6 +40,8 @@ import common
|
|||
import core
|
||||
import singleton
|
||||
import xhtml
|
||||
import weakref
|
||||
import timed_events
|
||||
|
||||
import multiuserchat as muc
|
||||
|
||||
|
@ -50,6 +52,8 @@ from contact import Contact, Resource
|
|||
from user import User
|
||||
from logger import logger
|
||||
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
SHOW_NAME = {
|
||||
'dnd': _('busy'),
|
||||
'away': _('away'),
|
||||
|
@ -245,6 +249,12 @@ class ChatTab(Tab):
|
|||
# we know that the remote user wants chatstates, or not.
|
||||
# None means we don’t know yet, and we send only "active" chatstates
|
||||
self.chatstate = None # can be "active", "composing", "paused", "gone", "inactive"
|
||||
# We keep a weakref of the event that will set our chatstate to "paused", so that
|
||||
# we can delete it or change it if we need to
|
||||
self.timed_event_paused = None
|
||||
# if that’s None, then no paused chatstate was sent recently
|
||||
# if that’s a weakref returning None, then a paused chatstate was sent
|
||||
# since the last input
|
||||
self.key_func['M-/'] = self.last_words_completion
|
||||
self.key_func['^M'] = self.on_enter
|
||||
self.commands['say'] = (self.command_say,
|
||||
|
@ -281,6 +291,7 @@ class ChatTab(Tab):
|
|||
"""
|
||||
Send an empty chatstate message
|
||||
"""
|
||||
state = '%s' % state
|
||||
msg = self.core.xmpp.make_message(self.get_name())
|
||||
msg['type'] = self.message_type
|
||||
msg['chat_state'] = state
|
||||
|
@ -294,9 +305,41 @@ class ChatTab(Tab):
|
|||
if config.get('send_chat_states', 'true') == 'true' and self.remote_wants_chatstates:
|
||||
if not empty_before and empty_after:
|
||||
self.send_chat_state("active")
|
||||
elif empty_before and not empty_after:
|
||||
elif (empty_before or (self.timed_event_paused is not None and not self.timed_event_paused())) and not empty_after:
|
||||
self.send_chat_state("composing")
|
||||
|
||||
def set_paused_delay(self, composing):
|
||||
"""
|
||||
we create a timed event that will put us to paused
|
||||
in a few seconds
|
||||
"""
|
||||
if config.get('send_chat_states', 'true') != 'true':
|
||||
return
|
||||
if self.timed_event_paused:
|
||||
# check the weakref
|
||||
event = self.timed_event_paused()
|
||||
if event:
|
||||
# the event already exists: we just update
|
||||
# its date
|
||||
event.change_date(datetime.now() + timedelta(seconds=4))
|
||||
return
|
||||
new_event = timed_events.DelayedEvent(4, self.send_chat_state, 'paused')
|
||||
self.core.add_timed_event(new_event)
|
||||
self.timed_event_paused = weakref.ref(new_event)
|
||||
|
||||
def cancel_paused_delay(self):
|
||||
"""
|
||||
Remove that event from the list and set it to None.
|
||||
Called for example when the input is emptied, or when the message
|
||||
is sent
|
||||
"""
|
||||
if self.timed_event_paused:
|
||||
event = self.timed_event_paused()
|
||||
if event:
|
||||
self.core.timed_events.remove(event)
|
||||
del event
|
||||
self.timed_event_paused = None
|
||||
|
||||
def command_say(self, line):
|
||||
raise NotImplementedError
|
||||
|
||||
|
@ -643,6 +686,9 @@ class MucTab(ChatTab):
|
|||
self.input.do_command(key)
|
||||
empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//'))
|
||||
self.send_composing_chat_state(empty_before, empty_after)
|
||||
if not empty_before and empty_after:
|
||||
self.cancel_paused_delay()
|
||||
self.set_paused_delay(empty_before and not empty_after)
|
||||
return False
|
||||
|
||||
def completion(self):
|
||||
|
@ -975,6 +1021,9 @@ class PrivateTab(ChatTab):
|
|||
self.input.do_command(key)
|
||||
empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//'))
|
||||
self.send_composing_chat_state(empty_before, empty_after)
|
||||
if not empty_before and empty_after:
|
||||
self.cancel_paused_delay()
|
||||
self.set_paused_delay(empty_before and not empty_after)
|
||||
return False
|
||||
|
||||
def on_lose_focus(self):
|
||||
|
@ -1399,6 +1448,9 @@ class ConversationTab(ChatTab):
|
|||
self.input.do_command(key)
|
||||
empty_after = self.input.get_text() == '' or (self.input.get_text().startswith('/') and not self.input.get_text().startswith('//'))
|
||||
self.send_composing_chat_state(empty_before, empty_after)
|
||||
if not empty_before and empty_after:
|
||||
self.cancel_paused_delay()
|
||||
self.set_paused_delay(empty_before and not empty_after)
|
||||
return False
|
||||
|
||||
def on_lose_focus(self):
|
||||
|
|
|
@ -31,6 +31,7 @@ class TimedEvent(object):
|
|||
Note that these events can NOT be used for very small delay or a very
|
||||
precise date, since the check for events is done once per second, as
|
||||
a maximum.
|
||||
The callback and its arguments should be passed as the lasts arguments.
|
||||
"""
|
||||
def __init__(self, date, callback, *args):
|
||||
self._callback = callback
|
||||
|
@ -57,12 +58,24 @@ class TimedEvent(object):
|
|||
else:
|
||||
return False
|
||||
|
||||
def change_date(self, date):
|
||||
"""
|
||||
Simply change the date of the event
|
||||
"""
|
||||
self.next_call_date = date
|
||||
|
||||
def add_delay(self, delay):
|
||||
"""
|
||||
Add a delay (in seconds) to the date
|
||||
"""
|
||||
self.next_call_date += datetime.timedelta(seconds=delay)
|
||||
|
||||
class DelayedEvent(TimedEvent):
|
||||
"""
|
||||
The date is calculated from now + a delay in seconds
|
||||
Use it if you want an event to happen in, e.g. 6 seconds
|
||||
"""
|
||||
def __init__(self, delay, callback, *args):
|
||||
date = datetime.datetime.now() + datetime.timedelta(0, delay)
|
||||
date = datetime.datetime.now() + datetime.timedelta(seconds=delay)
|
||||
TimedEvent.__init__(self, date, callback, args)
|
||||
|
||||
|
|
Loading…
Reference in a new issue