]>
git.saurik.com Git - wxWidgets.git/blob - wxPython/wx/lib/expando.py
f16e5e1d52f60db1546b855a1d925b979cd4f69f
1 #---------------------------------------------------------------------------
3 # Purpose: A multi-line text control that expands and collapses as more
4 # or less lines are needed to display its content.
8 # Created: 18-Sept-2006
10 # Copyright: (c) 2006 by Total Control Software
11 # Licence: wxWindows license
13 #---------------------------------------------------------------------------
15 This module contains the `ExpandoTextCtrl` which is a multi-line
16 text control that will expand its height on the fly to be able to show
17 all the lines of the content of the control.
21 import wx
.lib
.newevent
24 # This event class and binder object can be used to catch
25 # notifications that the ExpandoTextCtrl has resized itself and
26 # that layout adjustments may need to be made.
27 wxEVT_ETC_LAYOUT_NEEDED
= wx
.NewEventType()
28 EVT_ETC_LAYOUT_NEEDED
= wx
.PyEventBinder( wxEVT_ETC_LAYOUT_NEEDED
, 1 )
31 #---------------------------------------------------------------------------
33 class ExpandoTextCtrl(wx
.TextCtrl
):
35 The ExpandoTextCtrl is a multi-line wx.TextCtrl that will
36 adjust its height on the fly as needed to accomodate the number of
37 lines needed to display the current content of the control. It is
38 assumed that the width of the control will be a fixed value and
39 that only the height will be adjusted automatically. If the
40 control is used in a sizer then the width should be set as part of
41 the initial or min size of the control.
43 When the control resizes itself it will attempt to also make
44 necessary adjustments in the sizer hierarchy it is a member of (if
45 any) but if that is not suffiecient then the programmer can catch
46 the EVT_ETC_LAYOUT_NEEDED event in the container and make any
47 other layout adjustments that may be needed.
51 def __init__(self
, parent
, id=-1, value
="",
52 pos
=wx
.DefaultPosition
, size
=wx
.DefaultSize
,
53 style
=0, validator
=wx
.DefaultValidator
, name
="expando"):
54 # find the default height of a single line control
55 self
.defaultHeight
= self
._getDefaultHeight
(parent
)
56 # make sure we default to that height if none was given
59 h
= self
.defaultHeight
60 # always use the multi-line style
61 style
= style | wx
.TE_MULTILINE | wx
.TE_NO_VSCROLL | wx
.TE_RICH2
63 wx
.TextCtrl
.__init
__(self
, parent
, id, value
, pos
, (w
, h
),
64 style
, validator
, name
)
65 # save some basic metrics
66 self
.extraHeight
= self
.defaultHeight
- self
.GetCharHeight()
70 wx
.CallAfter(self
._adjustCtrl
)
72 self
.Bind(wx
.EVT_TEXT
, self
.OnTextChanged
)
75 def SetMaxHeight(self
, h
):
77 Sets the max height that the control will expand to on its
78 own, and adjusts it down if needed.
81 if h
!= -1 and self
.GetSize().height
> h
:
84 def GetMaxHeight(self
):
85 """Sets the max height that the control will expand to on its own"""
89 def SetFont(self
, font
):
90 wx
.TextCtrl
.SetFont(self
, font
)
95 def OnTextChanged(self
, evt
):
96 # check if any adjustments are needed on every text update
101 def _adjustCtrl(self
):
102 # if the current number of lines is different than before
103 # then recalculate the size needed and readjust
104 numLines
= self
.GetNumberOfLines()
105 if numLines
!= self
.numLines
:
106 self
.numLines
= numLines
107 charHeight
= self
.GetCharHeight()
108 height
= numLines
* charHeight
+ self
.extraHeight
109 if not (self
.maxHeight
!= -1 and height
> self
.maxHeight
):
110 # The size is changing... if the control is not in a
111 # sizer then we just want to change the size and
112 # that's it, the programmer will need to deal with
113 # potential layout issues. If it is being managed by
114 # a sizer then we'll change the min size setting and
115 # then try to do a layout. In either case we'll also
116 # send an event so the parent can handle any special
117 # layout issues that it wants to deal with.
118 if self
.GetContainingSizer() is not None:
119 mw
, mh
= self
.GetMinSize()
120 self
.SetMinSize((mw
, height
))
121 if self
.GetParent().GetSizer() is not None:
122 self
.GetParent().Layout()
124 self
.GetContainingSizer().Layout()
126 self
.SetSize((-1, height
))
127 # send notification that layout is needed
128 evt
= wx
.PyCommandEvent(wxEVT_ETC_LAYOUT_NEEDED
, self
.GetId())
129 evt
.SetEventObject(self
)
131 evt
.numLines
= numLines
132 self
.GetEventHandler().ProcessEvent(evt
)
135 def _getDefaultHeight(self
, parent
):
136 # checked for cached value
137 if self
.__class
__._defaultHeight
!= -1:
138 return self
.__class
__._defaultHeight
139 # otherwise make a single line textctrl and find out its default height
140 tc
= wx
.TextCtrl(parent
)
143 self
.__class
__._defaultHeight
= sz
.height
147 if wx
.VERSION
< (2,7) and 'wxGTK' in wx
.PlatformInfo
:
148 # the wxGTK version of GetNumberOfLines in 2.6 doesn't count
149 # wrapped lines, so we need to implement our own. This is
151 def GetNumberOfLines(self
):
152 text
= self
.GetValue()
153 width
= self
.GetSize().width
154 dc
= wx
.ClientDC(self
)
155 dc
.SetFont(self
.GetFont())
157 for line
in text
.split('\n'):
159 w
, h
= dc
.GetTextExtent(line
)
161 # the width of the text is wider than the control,
162 # calc how many lines it will be wrapped to
163 count
+= self
._wrapLine
(line
, dc
, width
)
170 def _wrapLine(self
, line
, dc
, width
):
171 # Estimate where the control will wrap the lines and
172 # return the count of extra lines needed.
173 pte
= dc
.GetPartialTextExtents(line
)
174 width
-= wx
.SystemSettings
.GetMetric(wx
.SYS_VSCROLL_X
)
179 while idx
< len(pte
):
182 if pte
[idx
] - start
> width
:
183 # we've reached the max width, add a new line
185 # did we see a space? if so restart the count at that pos
194 #---------------------------------------------------------------------------