#------------------------------------------------------------------------------
# Copyright (c) 2012, Enthought, Inc.
# All rights reserved.
#------------------------------------------------------------------------------
import wx
from enaml.validation.client_validators import null_validator, make_validator
from .wx_control import WxControl
class wxLineEdit(wx.TextCtrl):
""" A wx.TextCtrl subclass which is similar to a QLineEdit in terms
of features and behavior.
"""
def __init__(self, *args, **kwargs):
""" Initialize a wxLineEdit.
Parameters
----------
*args, **kwargs
The positional and keyword arguments to initialize a
wx.TextCtrl.
"""
super(wxLineEdit, self).__init__(*args, **kwargs)
self._placeholder_text = ''
self._placeholder_active = False
self._user_fgcolor = None
self.Bind(wx.EVT_KILL_FOCUS, self.OnKillFocus)
self.Bind(wx.EVT_SET_FOCUS, self.OnSetFocus)
#--------------------------------------------------------------------------
# Private API
#--------------------------------------------------------------------------
def _UpdatePlaceholderDisplay(self):
""" Updates the display with the placeholder text if no text
is currently set for the control.
"""
if not self.GetValue() and self._placeholder_text:
self.ChangeValue(self._placeholder_text)
color = wx.Color(95, 95, 95)
super(wxLineEdit, self).SetForegroundColour(color)
self._placeholder_active = True
def _RemovePlaceholderDisplay(self):
""" Removes the placeholder text if it is currently active.
"""
if self._placeholder_active:
self.ChangeValue('')
color = self._user_fgcolor or wx.Color(0, 0, 0)
super(wxLineEdit, self).SetForegroundColour(color)
self._placeholder_active = False
#--------------------------------------------------------------------------
# Event Handlers
#--------------------------------------------------------------------------
def OnKillFocus(self, event):
""" Refreshes the placeholder display when the control loses
focus.
"""
self._UpdatePlaceholderDisplay()
event.Skip()
def OnSetFocus(self, event):
""" Removes the placeholder display when the control receives
focus.
"""
self._RemovePlaceholderDisplay()
event.Skip()
#--------------------------------------------------------------------------
# Public API
#--------------------------------------------------------------------------
def GetBestSize(self):
""" Overridden best size method to add 44 pixels in width to the
field. This makes Wx consistent with Qt.
"""
size = super(wxLineEdit, self).GetBestSize()
return wx.Size(size.GetWidth() + 44, size.GetHeight())
def SetPlaceHolderText(self, placeholder_text):
""" Sets the placeholder text to the given value. Pass an empty
string to turn off the placeholder text functionality.
"""
self._placeholder_text = placeholder_text
self._UpdatePlaceholderDisplay()
def GetPlaceHolderText(self):
""" Returns the placeholder text for this control.
"""
return self._placeholder_text
def ChangeValue(self, text):
""" Overridden method which moves the insertion point to the end
of the field when changing the text value. This causes the field
to behave like Qt.
"""
super(wxLineEdit, self).ChangeValue(text)
self.SetInsertionPoint(len(text))
def GetValue(self):
""" Returns string value in the control, or an empty string if
the placeholder text is active.
"""
if self._placeholder_active:
return ''
return super(wxLineEdit, self).GetValue()
def SetForegroundColour(self, wxColor, force=False):
""" Sets the foreground color of the field. If the placeholder
text is being shown, `force` must be True in order to override
the placeholder text color.
"""
self._user_fgcolor = wxColor
if self._placeholder_active and not force:
return
super(wxLineEdit, self).SetForegroundColour(wxColor)
[docs]class WxField(WxControl):
""" A Wx implementation of an Enaml Field.
"""
#: The client side validator function for the field.
_validator = null_validator
#: The validator message for the validator.
_validator_message = ''
#: The list of submit triggers for when to submit a text change.
_submit_triggers = []
#: The last text value submitted to or sent from the server.
_last_value = None
#: A flag indicating whether the current field is invalid.
_is_error_state = False
#--------------------------------------------------------------------------
# Setup Methods
#--------------------------------------------------------------------------
[docs] def create(self, tree):
""" Create and initialize the wx field control.
"""
super(WxField, self).create(tree)
self.set_text(tree['text'])
self.set_validator(tree['validator'])
self.set_submit_triggers(tree['submit_triggers'])
self.set_placeholder(tree['placeholder'])
self.set_max_length(tree['max_length'])
widget = self.widget()
widget.Bind(wx.EVT_KILL_FOCUS, self.on_lost_focus)
widget.Bind(wx.EVT_TEXT_ENTER, self.on_return_pressed)
widget.Bind(wx.EVT_TEXT, self.on_text_edited)
#--------------------------------------------------------------------------
# Private API
#--------------------------------------------------------------------------
def _submit_text(self, text):
""" Submit the given text as an update to the server widget.
Parameters
----------
text : unicode
The unicode text to send to the server widget.
"""
content = {'text': text}
self.send_action('submit_text', content)
def _validate_and_submit(self):
""" Validate the current text in the control, and submit it to
the server widget if it's valid.
"""
text = self.widget().GetValue()
if text != self._last_value:
if self._validator(text):
self._clear_error_style()
self._submit_text(text)
self._last_value = text
else:
self._set_error_style()
def _set_error_style(self):
# A temporary hack until styles are implemented
self._is_error_state = True
# XXX attempting to change the field style here is futile
def _clear_error_style(self):
# A temporary hack until styles are implemented
self._is_error_state = False
# XXX attempting to change the field style here is futile
#--------------------------------------------------------------------------
# Event Handling
#--------------------------------------------------------------------------
[docs] def on_lost_focus(self, event):
""" The event handler for EVT_KILL_FOCUS event.
"""
event.Skip()
if 'lost_focus' in self._submit_triggers:
self._validate_and_submit()
[docs] def on_return_pressed(self, event):
""" The event handler for EVT_TEXT_ENTER event.
"""
# don't skip or Wx triggers the system beep, grrrrrrr.....
#event.Skip()
if 'return_pressed' in self._submit_triggers:
self._validate_and_submit()
def on_text_edited(self, event):
# Temporary kludge until styles are fully implemented
event.Skip()
widget = self.widget()
if self._validator(widget.GetValue()):
if self._is_error_state:
self._clear_error_style()
widget.SetToolTip(wx.ToolTip(''))
else:
if not self._is_error_state:
self._set_error_style()
widget.SetToolTip(wx.ToolTip(self._validator_message))
#--------------------------------------------------------------------------
# Message Handling
#--------------------------------------------------------------------------
[docs] def on_action_set_text(self, content):
""" Handle the 'set_text' action from the Enaml widget.
"""
self.set_text(content['text'])
[docs] def on_action_set_validator(self, content):
""" Handle the 'set_validator' action from the Enaml widget.
"""
self.set_validator(content['validator'])
[docs] def on_action_set_submit_triggers(self, content):
""" Handle the 'set_submit_triggers' action from the Enaml
widget.
"""
self.set_submit_triggers(content['sumbit_triggers'])
[docs] def on_action_set_placeholder(self, content):
""" Hanlde the 'set_placeholder' action from the Enaml widget.
"""
self.set_placeholder(content['placeholder'])
[docs] def on_action_set_echo_mode(self, content):
""" Handle the 'set_echo_mode' action from the Enaml widget.
"""
self.set_echo_mode(content['echo_mode'])
[docs] def on_action_set_max_length(self, content):
""" Handle the 'set_max_length' action from the Enaml widget.
"""
self.set_max_length(content['max_length'])
[docs] def on_action_set_read_only(self, content):
""" Handle the 'set_read_only' action from the Enaml widget.
"""
self.set_read_only(content['read_only'])
[docs] def on_action_invalid_text(self, content):
""" Handle the 'invalid_text' action from the Enaml widget.
"""
if self.widget().GetValue() == content['text']:
self._set_error_style()
#--------------------------------------------------------------------------
# Widget Update Methods
#--------------------------------------------------------------------------
[docs] def set_text(self, text):
""" Updates the text control with the given unicode text.
"""
self.widget().ChangeValue(text)
self._last_value = text
self._clear_error_style()
[docs] def set_validator(self, validator):
""" Sets the validator for the control.
"""
if validator is None:
self._validator = null_validator
self._validator_message = ''
else:
self._validator = make_validator(validator)
self._validator_message = validator.get('message', '')
[docs] def set_submit_triggers(self, triggers):
""" Set the submit triggers for the underlying widget.
"""
self._submit_triggers = triggers
[docs] def set_placeholder(self, placeholder):
""" Sets the placeholder text in the widget.
"""
self.widget().SetPlaceHolderText(placeholder)
[docs] def set_echo_mode(self, echo_mode):
""" Sets the echo mode of the wiget.
"""
# Wx cannot change the echo mode dynamically. It requires
# creating a brand-new control, so we just ignore the change.
pass
[docs] def set_max_length(self, max_length):
""" Set the max length of the control to max_length. If the max
length is <= 0 or > 32767 then the control will be set to hold
32kb of text.
"""
if (max_length <= 0) or (max_length > 32767):
max_length = 32767
self.widget().SetMaxLength(max_length)
[docs] def set_read_only(self, read_only):
""" Sets the read only state of the widget.
"""
# Wx cannot change the read only state dynamically. It requires
# creating a brand-new control, so we just ignore the change.
pass