From cad6c144bf9940aa672611a36f2174655c579af7 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Sat, 21 Jan 2017 13:03:40 -0800 Subject: Do not emit change until after text is updated Sometimes an Edit's change handler wants to edit the text that has just been entered. For instance, if the handler does validation, it may want to immediately highlight or prevent invalid entries from being shown. This is not possible if the change signal is emitted before the internal text data is updated as the update will overwrite anything that the change handler performed. Emitting the change event after the update has been performed allows for changes to be made. Fixes #212 --- urwid/widget.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/urwid/widget.py b/urwid/widget.py index 661e7ed..a3e39d8 100644 --- a/urwid/widget.py +++ b/urwid/widget.py @@ -1354,10 +1354,10 @@ class Edit(Text): """ text = self._normalize_to_caption(text) self.highlight = None - self._emit("change", text) self._edit_text = text if self.edit_pos > len(text): self.edit_pos = len(text) + self._emit("change", text) self._invalidate() def get_edit_text(self): -- cgit v1.2.1 From 4f85c63becc3267241b5940b32ed586a4da304a9 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Sat, 21 Jan 2017 14:47:14 -0800 Subject: Implement postchange signal for Edit widget. The Edit widget in 1.3.0 has a "change" signal which fires before the edit_text is updated. This means that change signal handlers cannot update the edit_text; the text is updated by the signal emitter after the change handler has finished running. To remedy this, in a backwards compatible way, create a second signal, "postchange" which fires after edit_text has been changed. "postchange" sends the widget's old text value as a parameter so that change handlers can use the former value if needed (for instance, to revert a change because the new_text was invalid) --- urwid/widget.py | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/urwid/widget.py b/urwid/widget.py index a3e39d8..945a77f 100644 --- a/urwid/widget.py +++ b/urwid/widget.py @@ -1101,11 +1101,22 @@ class Edit(Text): deletion. A caption may prefix the editing area. Uses text class for text layout. - Users of this class to listen for ``"change"`` events - sent when the value of edit_text changes. See :func:``connect_signal``. + Users of this class may listen for ``"change"`` or ``"postchange"`` + events. See :func:``connect_signal``. + + * ``"change"`` is sent just before the value of edit_text changes. + It receives the new text as an argument. Note that ``"change"`` cannot + change the text in question as edit_text changes the text afterwards. + * ``"postchange"`` is sent after the value of edit_text changes. + It receives the old value of the text as an argument and thus is + appropriate for changing the text. It is possible for a ``"postchange"`` + event handler to get into a loop of changing the text and then being + called when the event is re-emitted. It is up to the event + handler to guard against this case (for instance, by not changing the + text if it is signaled for for text that it has already changed once). """ # (this variable is picked up by the MetaSignals metaclass) - signals = ["change"] + signals = ["change", "postchange"] def valid_char(self, ch): """ @@ -1160,6 +1171,7 @@ class Edit(Text): self.allow_tab = allow_tab self._edit_pos = 0 self.set_caption(caption) + self._edit_text = '' self.set_edit_text(edit_text) if edit_pos is None: edit_pos = len(edit_text) @@ -1354,10 +1366,12 @@ class Edit(Text): """ text = self._normalize_to_caption(text) self.highlight = None + self._emit("change", text) + old_text = self._edit_text self._edit_text = text if self.edit_pos > len(text): self.edit_pos = len(text) - self._emit("change", text) + self._emit("postchange", old_text) self._invalidate() def get_edit_text(self): -- cgit v1.2.1 From 2dcc541c0130d3ed3854c60ff30381fcc3406349 Mon Sep 17 00:00:00 2001 From: Toshio Kuratomi Date: Sat, 21 Jan 2017 18:12:52 -0800 Subject: checkbox and radiobutton also needs to have a postchange event Just like Edit() boxes, CheckBox and RadioButton need to have an event emitted after the change has been commited. This allows us to modify the state after it occurs. (If the event only fires before the state is actually changed, the event handler cannot change the state because the emitter will change it to what they were going to after the event handler has finished. --- urwid/wimp.py | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/urwid/wimp.py b/urwid/wimp.py index 03a3613..70cf753 100755 --- a/urwid/wimp.py +++ b/urwid/wimp.py @@ -109,7 +109,7 @@ class CheckBox(WidgetWrap): # allow users of this class to listen for change events # sent when the state of this widget is modified # (this variable is picked up by the MetaSignals metaclass) - signals = ["change"] + signals = ["change", 'postchange'] def __init__(self, label, state=False, has_mixed=False, on_state_change=None, user_data=None): @@ -121,7 +121,7 @@ class CheckBox(WidgetWrap): function call for a single callback :param user_data: user_data for on_state_change - Signals supported: ``'change'`` + Signals supported: ``'change'``, ``"postchange"`` Register signal handler with:: @@ -233,7 +233,8 @@ class CheckBox(WidgetWrap): # self._state is None is a special case when the CheckBox # has just been created - if do_callback and self._state is not None: + old_state = self._state + if do_callback and old_state is not None: self._emit('change', state) self._state = state # rebuild the display widget with the new state @@ -241,6 +242,8 @@ class CheckBox(WidgetWrap): ('fixed', self.reserve_columns, self.states[state] ), self._label ] ) self._w.focus_col = 0 + if do_callback and old_state is not None: + self._emit('postchange', old_state) def get_state(self): """Return the state of the checkbox.""" @@ -335,7 +338,7 @@ class RadioButton(CheckBox): This function will append the new radio button to group. "first True" will set to True if group is empty. - Signals supported: ``'change'`` + Signals supported: ``'change'``, ``"postchange"`` Register signal handler with:: -- cgit v1.2.1