@@ -25,7 +25,7 @@ use crate::kurbo::{Line, Point, Rect, Vec2};
2525use crate :: piet:: TextLayout as _;
2626use crate :: shell:: text:: { Action as ImeAction , Event as ImeUpdate , InputHandler } ;
2727use crate :: widget:: prelude:: * ;
28- use crate :: { theme, Cursor , Env , Modifiers , Selector , TextAlignment , UpdateCtx } ;
28+ use crate :: { text , theme, Cursor , Env , Modifiers , Selector , TextAlignment , UpdateCtx } ;
2929
3030/// A widget that accepts text input.
3131///
@@ -564,28 +564,45 @@ impl<T: TextStorage + EditableText> EditSession<T> {
564564 fn do_action ( & mut self , buffer : & mut T , action : ImeAction ) {
565565 match action {
566566 ImeAction :: Move ( movement) => {
567- let sel = crate :: text:: movement ( movement, self . selection , & self . layout , false ) ;
567+ let sel = text:: movement ( movement, self . selection , & self . layout , false ) ;
568568 self . external_selection_change = Some ( sel) ;
569569 self . scroll_to_selection_end ( false ) ;
570570 }
571571 ImeAction :: MoveSelecting ( movement) => {
572- let sel = crate :: text:: movement ( movement, self . selection , & self . layout , true ) ;
572+ let sel = text:: movement ( movement, self . selection , & self . layout , true ) ;
573573 self . external_selection_change = Some ( sel) ;
574574 self . scroll_to_selection_end ( false ) ;
575575 }
576576 ImeAction :: SelectAll => {
577- let len = self . layout . text ( ) . as_ref ( ) . map ( |t| t . len ( ) ) . unwrap_or ( 0 ) ;
577+ let len = buffer . len ( ) ;
578578 self . external_selection_change = Some ( Selection :: new ( 0 , len) ) ;
579579 }
580- //ImeAction::SelectLine | ImeAction::SelectParagraph | ImeAction::SelectWord => {
581- //tracing::warn!("Line/Word selection actions are not implemented");
582- //}
580+ ImeAction :: SelectWord => {
581+ if self . selection . is_caret ( ) {
582+ let range =
583+ text:: movement:: word_range_for_pos ( buffer. as_str ( ) , self . selection . active ) ;
584+ self . external_selection_change = Some ( Selection :: new ( range. start , range. end ) ) ;
585+ }
586+
587+ // it is unclear what the behaviour should be if the selection
588+ // is not a caret (and may span multiple words)
589+ }
590+ // This requires us to have access to the layout, which might be stale?
591+ ImeAction :: SelectLine => ( ) ,
592+ // this assumes our internal selection is consistent with the buffer?
593+ ImeAction :: SelectParagraph => {
594+ if !self . selection . is_caret ( ) || buffer. len ( ) < self . selection . active {
595+ return ;
596+ }
597+ let prev = buffer. preceding_line_break ( self . selection . active ) ;
598+ let next = buffer. next_line_break ( self . selection . active ) ;
599+ self . external_selection_change = Some ( Selection :: new ( prev, next) ) ;
600+ }
583601 ImeAction :: Delete ( movement) if self . selection . is_caret ( ) => {
584602 if movement == Movement :: Grapheme ( druid_shell:: text:: Direction :: Upstream ) {
585603 self . backspace ( buffer) ;
586604 } else {
587- let to_delete =
588- crate :: text:: movement ( movement, self . selection , & self . layout , true ) ;
605+ let to_delete = text:: movement ( movement, self . selection , & self . layout , true ) ;
589606 self . selection = to_delete;
590607 self . ime_insert_text ( buffer, "" )
591608 }
@@ -643,7 +660,7 @@ impl<T: TextStorage + EditableText> EditSession<T> {
643660
644661 fn backspace ( & mut self , buffer : & mut T ) {
645662 let to_del = if self . selection . is_caret ( ) {
646- let del_start = crate :: text:: offset_for_delete_backwards ( & self . selection , buffer) ;
663+ let del_start = text:: offset_for_delete_backwards ( & self . selection , buffer) ;
647664 del_start..self . selection . anchor
648665 } else {
649666 self . selection . range ( )
@@ -680,26 +697,37 @@ impl<T: TextStorage + EditableText> EditSession<T> {
680697 }
681698
682699 fn sel_region_for_pos ( & mut self , pos : usize , click_count : u8 ) -> Range < usize > {
683- let text = match self . layout . text ( ) {
684- Some ( text) => text,
685- None => return pos..pos,
686- } ;
687700 match click_count {
688701 1 => pos..pos,
689- 2 => {
690- //FIXME: this doesn't handle whitespace correctly
691- let word_min = text. prev_word_offset ( pos) . unwrap_or ( 0 ) ;
692- let word_max = text. next_word_offset ( pos) . unwrap_or_else ( || text. len ( ) ) ;
693- word_min..word_max
694- }
702+ 2 => self . word_for_pos ( pos) ,
695703 _ => {
704+ let text = match self . layout . text ( ) {
705+ Some ( text) => text,
706+ None => return pos..pos,
707+ } ;
696708 let line_min = text. preceding_line_break ( pos) ;
697709 let line_max = text. next_line_break ( pos) ;
698710 line_min..line_max
699711 }
700712 }
701713 }
702714
715+ fn word_for_pos ( & self , pos : usize ) -> Range < usize > {
716+ let layout = match self . layout . layout ( ) {
717+ Some ( layout) => layout,
718+ None => return pos..pos,
719+ } ;
720+
721+ let line_n = layout. hit_test_text_position ( pos) . line ;
722+ let lm = layout. line_metric ( line_n) . unwrap ( ) ;
723+ let text = layout. line_text ( line_n) . unwrap ( ) ;
724+ let rel_pos = pos - lm. start_offset ;
725+ let mut range = text:: movement:: word_range_for_pos ( text, rel_pos) ;
726+ range. start += lm. start_offset ;
727+ range. end += lm. start_offset ;
728+ range
729+ }
730+
703731 fn update ( & mut self , ctx : & mut UpdateCtx , new_data : & T , env : & Env ) {
704732 if self
705733 . layout
0 commit comments