33// You may obtain a copy of the License in the LICENSE-APACHE file or at:
44// https://www.apache.org/licenses/LICENSE-2.0
55
6- //! The [`EditField`] and [` EditBox`] widgets, plus supporting items
6+ //! The [`EditBox`] widget
77
88use super :: * ;
99use crate :: edit:: highlight:: { Highlighter , Plain } ;
1010use crate :: { ScrollBar , ScrollBarMsg } ;
1111use kas:: event:: Scroll ;
1212use kas:: event:: components:: ScrollComponent ;
13- use kas:: messages:: { ReplaceSelectedText , SetValueText } ;
1413use kas:: prelude:: * ;
1514use kas:: theme:: { FrameStyle , TextClass } ;
1615use std:: fmt:: { Debug , Display } ;
@@ -21,17 +20,43 @@ mod EditBox {
2120 /// A text-edit box
2221 ///
2322 /// A single- or multi-line editor for unformatted text.
24- /// See also notes on [`EditField`].
2523 ///
2624 /// By default, the editor supports a single-line only;
2725 /// [`Self::with_multi_line`] can be used to change this.
2826 ///
27+ /// ### Event handling
28+ ///
29+ /// This widget attempts to handle all standard text-editor input and scroll
30+ /// events.
31+ ///
32+ /// Key events for moving the edit cursor (e.g. arrow keys) are consumed
33+ /// only if the edit cursor is moved while key events for adjusting or using
34+ /// the selection (e.g. `Command::Copy` and `Command::Deselect`)
35+ /// are consumed only when a selection exists. In contrast, key events for
36+ /// inserting or deleting text are always consumed.
37+ ///
38+ /// [`Command::Enter`] inserts a line break in multi-line mode, but in
39+ /// single-line mode or if the <kbd>Shift</kbd> key is held it is treated
40+ /// the same as [`Command::Activate`].
41+ ///
42+ /// ### Performance and limitations
43+ ///
44+ /// Text representation is via a single [`String`]. Edit operations are
45+ /// `O(n)` where `n` is the length of text (with text layout algorithms
46+ /// having greater cost than copying bytes in the backing [`String`]).
47+ /// This isn't necessarily *slow*; when run with optimizations the type can
48+ /// handle type-setting around 20kB of UTF-8 in under 10ms (with significant
49+ /// scope for optimization, given that currently layout is re-run from
50+ /// scratch on each key stroke). Regardless, this approach is not designed
51+ /// to scale to handle large documents via a single `EditBox` widget.
52+ ///
2953 /// ### Messages
3054 ///
31- /// [`SetValueText`] may be used to replace the entire text and
32- /// [`ReplaceSelectedText`] may be used to replace selected text when this
33- /// widget is [editable](Editor::is_editable). This triggers the action
34- /// handlers [`EditGuard::edit`] followed by [`EditGuard::activate`].
55+ /// [`kas::messages::SetValueText`] may be used to replace the entire text
56+ /// and [`kas::messages::ReplaceSelectedText`] may be used to replace
57+ /// selected text when this widget is not [read-only](Editor::is_read_only).
58+ /// Both add an item to the undo history and invoke the action handler
59+ /// [`EditGuard::edit`].
3560 ///
3661 /// [`kas::messages::SetScrollOffset`] may be used to set the scroll offset.
3762 #[ autoimpl( Debug where G : trait , H : trait ) ]
@@ -42,9 +67,10 @@ mod EditBox {
4267 scroll : ScrollComponent ,
4368 // NOTE: inner is a Viewport which doesn't use update methods, therefore we don't call them.
4469 #[ widget]
45- inner : EditField < G , H > ,
70+ inner : EditBoxCore < G , H > ,
4671 #[ widget( & ( ) ) ]
4772 vert_bar : ScrollBar < kas:: dir:: Down > ,
73+ frame_style : FrameStyle ,
4874 frame_offset : Offset ,
4975 frame_size : Size ,
5076 frame_offset_ex_margin : Offset ,
@@ -64,7 +90,7 @@ mod EditBox {
6490 rules. append ( bar_rules) ;
6591 }
6692
67- let frame_rules = cx. frame ( FrameStyle :: EditBox , axis) ;
93+ let frame_rules = cx. frame ( self . frame_style , axis) ;
6894 self . frame_offset_ex_margin
6995 . set_component ( axis, frame_rules. size ( ) ) ;
7096 let ( rules, offset, size) = frame_rules. surround ( rules) ;
@@ -111,7 +137,7 @@ mod EditBox {
111137 let mut draw_inner = draw. re ( ) ;
112138 draw_inner. set_id ( self . inner . id ( ) ) ;
113139 let bg = self . inner . background_color ( ) ;
114- draw_inner. frame ( self . rect ( ) , FrameStyle :: EditBox , bg) ;
140+ draw_inner. frame ( self . rect ( ) , self . frame_style , bg) ;
115141
116142 self . inner
117143 . draw_with_offset ( draw. re ( ) , self . clip_rect , self . scroll . offset ( ) ) ;
@@ -170,29 +196,18 @@ mod EditBox {
170196 }
171197
172198 fn handle_messages ( & mut self , cx : & mut EventCx < ' _ > , data : & G :: Data ) {
173- let action = if cx. last_child ( ) == Some ( widget_index ! [ self . vert_bar] )
199+ let offset = if cx. last_child ( ) == Some ( widget_index ! [ self . vert_bar] )
174200 && let Some ( ScrollBarMsg ( y) ) = cx. try_pop ( )
175201 {
176- let offset = Offset ( self . scroll . offset ( ) . 0 , y) ;
177- self . scroll . set_offset ( offset)
202+ Offset ( self . scroll . offset ( ) . 0 , y)
178203 } else if let Some ( kas:: messages:: SetScrollOffset ( offset) ) = cx. try_pop ( ) {
179- self . scroll . set_offset ( offset)
180- } else if self . is_editable ( )
181- && let Some ( SetValueText ( string) ) = cx. try_pop ( )
182- {
183- self . edit ( cx, data, |edit, cx| {
184- edit. pre_commit ( ) ;
185- edit. set_string ( cx, string) ;
186- } ) ;
187- return ;
188- } else if let Some ( & ReplaceSelectedText ( _) ) = cx. try_peek ( ) {
189- self . inner . handle_messages ( cx, data) ;
190- return ;
204+ offset
191205 } else {
206+ self . inner . handle_messages ( cx, data) ;
192207 return ;
193208 } ;
194209
195- if let Some ( moved) = action {
210+ if let Some ( moved) = self . scroll . set_offset ( offset ) {
196211 cx. action_moved ( moved) ;
197212 self . update_scroll_offset ( cx) ;
198213 }
@@ -232,8 +247,9 @@ mod EditBox {
232247 EditBox {
233248 core : Default :: default ( ) ,
234249 scroll : Default :: default ( ) ,
235- inner : EditField :: new ( guard) ,
250+ inner : EditBoxCore :: new ( guard) ,
236251 vert_bar : Default :: default ( ) ,
252+ frame_style : FrameStyle :: EditBox ,
237253 frame_offset : Default :: default ( ) ,
238254 frame_size : Default :: default ( ) ,
239255 frame_offset_ex_margin : Default :: default ( ) ,
@@ -254,6 +270,7 @@ mod EditBox {
254270 scroll : self . scroll ,
255271 inner : self . inner . with_highlighter ( highlighter) ,
256272 vert_bar : self . vert_bar ,
273+ frame_style : self . frame_style ,
257274 frame_offset : self . frame_offset ,
258275 frame_size : self . frame_size ,
259276 frame_offset_ex_margin : self . frame_offset_ex_margin ,
@@ -267,6 +284,15 @@ mod EditBox {
267284 self . inner . set_highlighter ( highlighter) ;
268285 }
269286
287+ /// Replace the frame style
288+ ///
289+ /// The default is [`FrameStyle::EditBox`].
290+ #[ inline]
291+ pub fn with_frame_style ( mut self , style : FrameStyle ) -> Self {
292+ self . frame_style = style;
293+ self
294+ }
295+
270296 fn update_content_size ( & mut self , cx : & mut EventState ) {
271297 if !self . core . status . is_sized ( ) {
272298 return ;
@@ -301,15 +327,15 @@ impl<A: 'static> EditBox<DefaultGuard<A>> {
301327 #[ inline]
302328 pub fn text < S : ToString > ( text : S ) -> Self {
303329 EditBox {
304- inner : EditField :: text ( text) ,
330+ inner : EditBoxCore :: text ( text) ,
305331 ..Default :: default ( )
306332 }
307333 }
308334
309335 /// Construct a read-only `EditBox` displaying some `String` value
310336 #[ inline]
311337 pub fn string ( value_fn : impl Fn ( & A ) -> String + Send + ' static ) -> EditBox < StringGuard < A > > {
312- EditBox :: new ( StringGuard :: new ( value_fn) ) . with_editable ( false )
338+ EditBox :: new ( StringGuard :: new ( value_fn) ) . with_read_only ( true )
313339 }
314340
315341 /// Construct an `EditBox` for a parsable value (e.g. a number)
@@ -356,14 +382,14 @@ impl<A: 'static> EditBox<StringGuard<A>> {
356382 /// The `msg_fn` is called when the field is activated (<kbd>Enter</kbd>)
357383 /// and when it loses focus after content is changed.
358384 ///
359- /// This method sets self as editable (see [`Self::with_editable `]).
385+ /// This method sets self as editable (see [`Self::with_read_only `]).
360386 #[ must_use]
361387 pub fn with_msg < M > ( mut self , msg_fn : impl Fn ( & str ) -> M + Send + ' static ) -> Self
362388 where
363389 M : Debug + ' static ,
364390 {
365391 self . inner . guard = self . inner . guard . with_msg ( msg_fn) ;
366- self . inner = self . inner . with_editable ( true ) ;
392+ self . inner = self . inner . with_read_only ( false ) ;
367393 self
368394 }
369395}
@@ -379,11 +405,11 @@ impl<G: EditGuard, H: Highlighter> EditBox<G, H> {
379405 self
380406 }
381407
382- /// Set whether this widget is editable (inline)
408+ /// Set whether this `EditBox` is read-only (inline)
383409 #[ inline]
384410 #[ must_use]
385- pub fn with_editable ( mut self , editable : bool ) -> Self {
386- self . inner = self . inner . with_editable ( editable ) ;
411+ pub fn with_read_only ( mut self , read_only : bool ) -> Self {
412+ self . inner = self . inner . with_read_only ( read_only ) ;
387413 self
388414 }
389415
0 commit comments