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 #260 from enthought/feature-multiline
Browse files Browse the repository at this point in the history
Feature multiline
  • Loading branch information
sccolbert committed Feb 13, 2013
2 parents 67133d3 + 400f7e2 commit 62bbb67
Show file tree
Hide file tree
Showing 8 changed files with 382 additions and 10 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
114 changes: 114 additions & 0 deletions enaml/qt/qt_multiline_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,114 @@
#------------------------------------------------------------------------------
# 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 QMultilineEdit(QTextEdit):
""" A QTextEdit which notifies on a collapsing timer.
"""
delayedTextChanged = Signal()

def __init__(self, parent=None):
super(QMultilineEdit, self).__init__(parent)
self._changed_timer = timer = QTimer()
timer.setInterval(200)
timer.setSingleShot(True)
timer.timeout.connect(self.delayedTextChanged)
self.textChanged.connect(timer.start)


class QtMultilineField(QtControl):
""" A Qt4 implementation of an Enaml Field.
"""
#: Whether or not to auto synchronize the text on change.
_auto_sync_text = True

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

def create(self, tree):
""" Create and initialize the underlying widget.
"""
super(QtMultilineField, self).create(tree)
self._auto_sync_text = tree['auto_sync_text']
self.set_text(tree['text'])
self.set_read_only(tree['read_only'])
widget = self.widget()
widget.delayedTextChanged.connect(self.on_text_changed)

#--------------------------------------------------------------------------
# Private API
#--------------------------------------------------------------------------
def _send_text_changed(self):
""" Send the current text as an update to the server widget.
"""
text = self.widget().toPlainText()
self.send_action('text_changed', {'text': text})

#--------------------------------------------------------------------------
# Signal Handlers
#--------------------------------------------------------------------------
def on_text_changed(self):
""" The signal handler for 'delayedTextChanged' signal.
"""
if self._auto_sync_text and 'text' not in self.loopback_guard:
self._send_text_changed()

#--------------------------------------------------------------------------
# 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_auto_sync_text(self, content):
""" Handle the 'set_auto_sync_text' action from the Enaml widget.
"""
self._auto_sync_text = content['auto_sync_text']

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

def on_action_sync_text(self, content):
""" Handle the 'sync_text' action from the Enaml widget.
"""
self._send_text_changed()

#--------------------------------------------------------------------------
# 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_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
68 changes: 68 additions & 0 deletions enaml/widgets/multiline_field.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
#------------------------------------------------------------------------------
# Copyright (c) 2013, Enthought, Inc.
# All rights reserved.
#------------------------------------------------------------------------------
from traits.api import Bool, Unicode

from .control import Control


class MultilineField(Control):
""" A simple multiline editable text widget.
"""
#: The unicode text to display in the field.
text = Unicode

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

#: Whether the text in the control should be auto-synchronized with
#: the text attribute on the field. If this is True, the text will
#: be updated every time the user edits the control. In order to be
#: efficient, the toolkit will batch updates on a collapsing timer.
auto_sync_text = Bool(True)

#: Multiline fields expand freely in width and height by default.
hug_width = 'ignore'
hug_height = 'ignore'

#--------------------------------------------------------------------------
# Initialization
#--------------------------------------------------------------------------
def snapshot(self):
""" Get the snapshot dict for the control.
"""
snap = super(MultilineField, self).snapshot()
snap['text'] = self.text
snap['read_only'] = self.read_only
snap['auto_sync_text'] = self.auto_sync_text
return snap

def bind(self):
""" Bind the change handlers for the control.
"""
super(MultilineField, self).bind()
self.publish_attributes('text', 'read_only', 'auto_sync_text')

#--------------------------------------------------------------------------
# Message Handling
#--------------------------------------------------------------------------
def on_action_text_changed(self, content):
""" Handle the 'text_changed' action from the client widget.
"""
text = content['text']
self.set_guarded(text=text)

#--------------------------------------------------------------------------
# Public API
#--------------------------------------------------------------------------
def sync_text(self):
""" Send a message to the toolkit to synchronize the text.
"""
self.send_action('sync_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
18 changes: 9 additions & 9 deletions enaml/wx/wx_group_box.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ def __init__(self, *args, **kwargs):
Parameters
----------
*args, **kwargs
The positional and keyword arguments to initialize a
The positional and keyword arguments to initialize a
wxContainer.
"""
Expand All @@ -37,15 +37,15 @@ def __init__(self, *args, **kwargs):
self._label_size = self._label.GetBestSize()
self._title_alignment = wx.ALIGN_LEFT
self._flat = False
# Set the panel to double buffered or suffer terrible
# Set the panel to double buffered or suffer terrible
# rendering artifacts
self.SetDoubleBuffered(True)

#--------------------------------------------------------------------------
# Public API
#--------------------------------------------------------------------------
def GetAlignment(self):
""" Return the wx alignment flag for the current alignment
""" Return the wx alignment flag for the current alignment
of the group box title.
"""
Expand All @@ -58,7 +58,7 @@ def SetAlignment(self, alignment):
"""
self._title_alignment = alignment
self._update_layout()

def GetFlat(self):
""" Returns a boolean indicating whether the group box is using
a flat style.
Expand All @@ -82,7 +82,7 @@ def SetFlat(self, flat):

def GetTitle(self):
""" Return the current title text in the group box.
"""
# Undo the hack applied in SetTitle(...)
title = self._title
Expand Down Expand Up @@ -144,7 +144,7 @@ def GetContentsMargins(self):
# Private API
#--------------------------------------------------------------------------
def _update_layout(self):
""" Synchronizes the drawing of the group box decorations with
""" Synchronizes the drawing of the group box decorations with
the panel.
"""
Expand All @@ -161,7 +161,7 @@ def _update_border_geometry(self):
"""
width, height = self.GetSizeTuple()
self._border.SetSizeWH(width, height)

def _update_line_geometry(self):
""" Updates the geometry of the line.
Expand Down Expand Up @@ -238,7 +238,7 @@ def on_action_set_title(self, content):
self.set_title(content['title'])
new_margins = widget.GetContentsMargins()
if old_margins != new_margins:
self.refresh_contents_constraints()
self.contents_margins_updated()

def on_action_set_title_align(self, content):
""" Handle the 'set_title_align' action from the Enaml widget.
Expand Down Expand Up @@ -266,7 +266,7 @@ def set_flat(self, flat):
"""
self.widget().SetFlat(flat)

def set_title_align(self, align):
""" Updates the alignment of the title of the group box.
Expand Down
Loading

0 comments on commit 62bbb67

Please sign in to comment.