@@ -10,9 +10,12 @@ use bevy_app::{App, Plugin};
1010use bevy_ecs:: prelude:: * ;
1111use bevy_input:: keyboard:: { Key , KeyCode , KeyboardInput } ;
1212use bevy_input:: ButtonInput ;
13- use bevy_input_focus:: FocusedInput ;
13+ use bevy_input_focus:: { FocusedInput , InputFocus } ;
14+ use bevy_picking:: events:: { Drag , Pointer , Press } ;
15+ use bevy_picking:: pointer:: PointerButton ;
1416use bevy_text:: { EditableText , TextEdit } ;
1517use bevy_ui:: { widget:: TextNodeFlags , ContentSize , Node } ;
18+ use bevy_ui:: { ComputedNode , ComputedUiRenderTargetInfo , UiGlobalTransform , UiScale } ;
1619use smol_str:: SmolStr ;
1720
1821/// System that processes keyboard input events into text edit actions for focused [`EditableText`] widgets.
@@ -68,6 +71,89 @@ fn on_focused_keyboard_input(
6871 trigger. propagate ( should_propagate) ;
6972}
7073
74+ /// System that processes pointer press events into text edit actions for [`EditableText`] widgets.
75+ ///
76+ /// Note that this does not immediately apply the edits; they are queued up in [`EditableText::pending_edits`],
77+ /// and then applied later by the [`apply_text_edits`](`bevy_text::apply_text_edits`) system.
78+ fn on_pointer_press (
79+ mut press : On < Pointer < Press > > ,
80+ mut text_input_query : Query < (
81+ & mut EditableText ,
82+ & ComputedNode ,
83+ & ComputedUiRenderTargetInfo ,
84+ & UiGlobalTransform ,
85+ ) > ,
86+ keys : Res < ButtonInput < Key > > ,
87+ mut input_focus : ResMut < InputFocus > ,
88+ ui_scale : Res < UiScale > ,
89+ ) {
90+ if press. button != PointerButton :: Primary {
91+ return ;
92+ }
93+
94+ let Ok ( ( mut editable_text, node, target, transform) ) = text_input_query. get_mut ( press. entity )
95+ else {
96+ return ;
97+ } ;
98+
99+ let Some ( local_pos) = transform. try_inverse ( ) . map ( |inverse| {
100+ inverse
101+ . transform_point2 ( press. pointer_location . position * target. scale_factor ( ) / ui_scale. 0 )
102+ + 0.5 * node. size ( )
103+ } ) else {
104+ return ;
105+ } ;
106+
107+ editable_text
108+ . pending_edits
109+ . push ( if keys. pressed ( Key :: Shift ) {
110+ TextEdit :: ShiftClickExtension
111+ } else {
112+ TextEdit :: MoveToPoint
113+ } ( local_pos) ) ;
114+
115+ input_focus. set ( press. entity ) ;
116+
117+ press. propagate ( false ) ;
118+ }
119+
120+ /// System that processes pointer drag events into text edit actions for [`EditableText`] widgets.
121+ ///
122+ /// Note that this does not immediately apply the edits; they are queued up in [`EditableText::pending_edits`],
123+ /// and then applied later by the [`apply_text_edits`](`bevy_text::apply_text_edits`) system.
124+ fn on_pointer_drag (
125+ mut drag : On < Pointer < Drag > > ,
126+ mut text_input_query : Query < (
127+ & mut EditableText ,
128+ & ComputedNode ,
129+ & ComputedUiRenderTargetInfo ,
130+ & UiGlobalTransform ,
131+ ) > ,
132+ ui_scale : Res < UiScale > ,
133+ ) {
134+ if drag. button != PointerButton :: Primary {
135+ return ;
136+ }
137+
138+ let Ok ( ( mut editable_text, node, target, transform) ) = text_input_query. get_mut ( drag. entity )
139+ else {
140+ return ;
141+ } ;
142+
143+ let Some ( local_pos) = transform. try_inverse ( ) . map ( |inverse| {
144+ inverse
145+ . transform_point2 ( drag. pointer_location . position * target. scale_factor ( ) / ui_scale. 0 )
146+ + 0.5 * node. size ( )
147+ } ) else {
148+ return ;
149+ } ;
150+
151+ editable_text
152+ . pending_edits
153+ . push ( TextEdit :: ExtendSelectionToPoint ( local_pos) ) ;
154+ drag. propagate ( false ) ;
155+ }
156+
71157/// Enables support for the [`EditableText`] widget.
72158///
73159/// Contains the systems and observers necessary to update widget state and handle user input.
@@ -81,7 +167,9 @@ pub struct EditableTextInputPlugin;
81167
82168impl Plugin for EditableTextInputPlugin {
83169 fn build ( & self , app : & mut App ) {
84- app. add_observer ( on_focused_keyboard_input) ;
170+ app. add_observer ( on_focused_keyboard_input)
171+ . add_observer ( on_pointer_drag)
172+ . add_observer ( on_pointer_press) ;
85173
86174 // These components cannot be registered in `bevy_text` where `EditableText` is defined,
87175 // because that would create a circular dependency between `bevy_text` and `bevy_ui`.
0 commit comments