Skip to content
This repository has been archived by the owner on May 24, 2021. It is now read-only.

Commit

Permalink
Merge pull request #259 from nmichaud/feature-multiline-edit
Browse files Browse the repository at this point in the history
Feature multiline edit
  • Loading branch information
sccolbert committed Feb 13, 2013
2 parents 67133d3 + f313263 commit dde4bd3
Show file tree
Hide file tree
Showing 7 changed files with 429 additions and 2 deletions.
6 changes: 6 additions & 0 deletions enaml/qt/qt_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -142,6 +142,11 @@ def mpl_canvas_factory():
return QtMPLCanvas


def multiline_field_factory():
from .qt_multiline_field import QtMultiLineField
return QtMultiLineField


def notebook_factory():
from .qt_notebook import QtNotebook
return QtNotebook
Expand Down Expand Up @@ -269,6 +274,7 @@ def register_default():
register('Menu', menu_factory)
register('MenuBar', menu_bar_factory)
register('MPLCanvas', mpl_canvas_factory)
register('MultiLineField', multiline_field_factory)
register('Notebook', notebook_factory)
register('Page', page_factory)
register('PushButton', push_button_factory)
Expand Down
136 changes: 136 additions & 0 deletions enaml/qt/qt_multiline_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
#------------------------------------------------------------------------------
# Copyright (c) 2013, Enthought, Inc.
# All rights reserved.
#------------------------------------------------------------------------------
from .qt.QtGui import QTextEdit
from .qt.QtCore import Signal, QTimer
from .qt_control import QtControl


class QFocusMultiLineEdit(QTextEdit):
""" A QTextEdit subclass which converts a lost focus event into
a lost focus signal.
"""
lostFocus = Signal()

delayedTextChanged = Signal()

def __init__(self, parent=None):
super(QFocusMultiLineEdit, self).__init__(parent)

# Set up a collapsing timer to fire 200 milliseconds
# after the text is changed
self._changed_timer = timer = QTimer()
timer.setInterval(200)
timer.setSingleShot(True)
timer.timeout.connect(self.delayedTextChanged)

# Connect the timer to the text changed signal
self.textChanged.connect(timer.start)

def focusOutEvent(self, event):
self.lostFocus.emit()
return super(QFocusMultiLineEdit, self).focusOutEvent(event)


class QtMultiLineField(QtControl):
""" A Qt4 implementation of an Enaml Field.
"""
#: The list of submit triggers for when to submit a text change.
_submit_triggers = []

#--------------------------------------------------------------------------
# Setup Methods
#--------------------------------------------------------------------------
def create_widget(self, parent, tree):
""" Creates the underlying QFocusMultiLineEdit widget.
"""
return QFocusMultiLineEdit(parent)

def create(self, tree):
""" Create and initialize the underlying widget.
"""
super(QtMultiLineField, self).create(tree)
self.set_text(tree['text'])
self.set_submit_triggers(tree['submit_triggers'])
self.set_read_only(tree['read_only'])
widget = self.widget()
widget.lostFocus.connect(self.on_lost_focus)
widget.delayedTextChanged.connect(self.on_text_changed)

#--------------------------------------------------------------------------
# Private API
#--------------------------------------------------------------------------
def _submit_text(self):
""" Submit the given text as an update to the server widget.
"""
text = self.widget().toPlainText()
content = {'text': text}
self.send_action('submit_text', content)

#--------------------------------------------------------------------------
# Signal Handlers
#--------------------------------------------------------------------------
def on_lost_focus(self):
""" The signal handler for 'lostFocus' signal.
"""
if 'lost_focus' in self._submit_triggers:
self._submit_text()

def on_text_changed(self):
""" The signal handler for 'delayedTextChanged' signal.
"""
if 'text_changed' in self._submit_triggers:
self._submit_text()

#--------------------------------------------------------------------------
# Message Handlers
#--------------------------------------------------------------------------
def on_action_set_text(self, content):
""" Handle the 'set_text' action from the Enaml widget.
"""
self.set_text(content['text'])

def on_action_set_submit_triggers(self, content):
""" Handle the 'set_submit_triggers' action from the Enaml
widget.
"""
self.set_submit_triggers(content['submit_triggers'])

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'])

#--------------------------------------------------------------------------
# Widget Update Methods
#--------------------------------------------------------------------------
def set_text(self, text):
""" Set the text in the underlying widget.
"""
with self.loopback_guard('text'):
self.widget().setText(text)

def set_submit_triggers(self, triggers):
""" Set the submit triggers for the underlying widget.
"""
self._submit_triggers = triggers

def set_read_only(self, read_only):
""" Set whether or not the widget is read only.
"""
self.widget().setEnabled(not read_only)

1 change: 1 addition & 0 deletions enaml/widgets/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@
from .menu import Menu
from .menu_bar import MenuBar
from .mpl_canvas import MPLCanvas
from .multiline_field import MultiLineField
from .notebook import Notebook
from .page import Page
from .progress_bar import ProgressBar
Expand Down
78 changes: 78 additions & 0 deletions enaml/widgets/multiline_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
#------------------------------------------------------------------------------
# Copyright (c) 2013, Enthought, Inc.
# All rights reserved.
#------------------------------------------------------------------------------
from traits.api import Bool, Unicode, Enum, List

from enaml.validation.validator import Validator

from .control import Control


class MultiLineField(Control):
""" A multi line editable text widget.
"""
#: The unicode text to display in the field.
text = Unicode

#: The list of actions which should cause the client to submit its
#: text to the server for validation and update. The currently
#: supported values are 'lost_focus' and 'return_pressed'.
submit_triggers = List(
Enum('lost_focus', 'text_changed'), ['lost_focus', 'text_changed']
)

#: Whether or not the field is read only. Defaults to False.
read_only = Bool(False)

#: How strongly a component hugs it's contents' width. Text boxes ignore
#: both the width and height hugs by default, so they expand freely in both
#: directions
hug_width = 'ignore'
hug_height = 'ignore'

#--------------------------------------------------------------------------
# Initialization
#--------------------------------------------------------------------------
def snapshot(self):
""" Returns the snapshot dict for the textedit
"""
snap = super(MultiLineField, self).snapshot()
snap['text'] = self.text
snap['submit_triggers'] = self.submit_triggers
snap['read_only'] = self.read_only
return snap

def bind(self):
""" A method called after initialization which allows the widget
to bind any event handlers necessary.
"""
super(MultiLineField, self).bind()
attrs = (
'text', 'read_only',
)
self.publish_attributes(*attrs)
self.on_trait_change(self._send_submit_triggers, 'submit_triggers[]')

#--------------------------------------------------------------------------
# Private API
#--------------------------------------------------------------------------
def _send_submit_triggers(self):
""" Send the new submit triggers to the client widget.
"""
content = {'submit_triggers': self.submit_triggers}
self.send_action('set_submit_triggers', content)

#--------------------------------------------------------------------------
# Message Handling
#--------------------------------------------------------------------------
def on_action_submit_text(self, content):
""" Handle the 'submit_text' action from the client widget.
"""
text = content['text']
self.set_guarded(text=text)
6 changes: 6 additions & 0 deletions enaml/wx/wx_factories.py
Original file line number Diff line number Diff line change
Expand Up @@ -102,6 +102,11 @@ def mpl_canvas_factory():
return WxMPLCanvas


def multiline_field_factory():
from wx_multiline_field import WxMultiLineField
return WxMultiLineField


def notebook_factory():
from .wx_notebook import WxNotebook
return WxNotebook
Expand Down Expand Up @@ -194,6 +199,7 @@ def register_default():
register('Menu', menu_factory)
register('MenuBar', menu_bar_factory)
register('MPLCanvas', mpl_canvas_factory)
register('MultiLineField', multiline_field_factory)
register('Notebook', notebook_factory)
register('Page', page_factory)
register('PushButton', push_button_factory)
Expand Down
Loading

0 comments on commit dde4bd3

Please sign in to comment.