3
3
4
4
package software .aws .toolkits .eclipse .amazonq .inlineChat ;
5
5
6
+ import java .util .Arrays ;
7
+ import java .util .HashSet ;
6
8
import java .util .Optional ;
7
9
import java .util .concurrent .CompletableFuture ;
8
10
import java .util .concurrent .atomic .AtomicReference ;
27
29
import org .eclipse .swt .widgets .Composite ;
28
30
import org .eclipse .swt .widgets .Control ;
29
31
import org .eclipse .swt .widgets .Display ;
32
+ import org .eclipse .swt .widgets .Listener ;
30
33
import org .eclipse .swt .widgets .Text ;
31
34
32
35
import software .aws .toolkits .eclipse .amazonq .chat .models .CursorState ;
33
36
import software .aws .toolkits .eclipse .amazonq .plugin .Activator ;
34
37
import software .aws .toolkits .eclipse .amazonq .util .Constants ;
35
- import software .aws .toolkits .eclipse .amazonq .util .PluginPlatform ;
36
- import software .aws .toolkits .eclipse .amazonq .util .PluginUtils ;
37
38
import software .aws .toolkits .eclipse .amazonq .util .ToolkitNotification ;
38
39
39
40
public final class InlineChatUIManager {
@@ -52,6 +53,7 @@ public final class InlineChatUIManager {
52
53
private final String decidingMessage = "Accept (Enter) | Reject (Esc)" ;
53
54
private boolean isDarkTheme ;
54
55
private int latestOffset ;
56
+ private Listener paintListenerRef = null ;
55
57
56
58
private InlineChatUIManager () {
57
59
// Prevent instantiation
@@ -134,53 +136,41 @@ protected Control createDialogArea(final Composite parent) {
134
136
var composite = (Composite ) super .createDialogArea (parent );
135
137
composite .setLayout (new GridLayout (1 , false ));
136
138
137
- inputField = new Text (composite , SWT .SEARCH | SWT .BORDER | SWT .SINGLE );
138
- if (PluginUtils .getPlatform () == PluginPlatform .WINDOWS ) {
139
- Display .getDefault ().asyncExec (() -> {
140
- inputField .setForeground (Display .getDefault ().getSystemColor (SWT .COLOR_GRAY ));
141
- inputField .setText (inputPromptMessage );
142
- });
139
+ inputField = new Text (composite , SWT .BORDER | SWT .MULTI );
140
+ Display .getDefault ().asyncExec (() -> {
141
+ inputField .setForeground (Display .getDefault ().getSystemColor (SWT .COLOR_GRAY ));
142
+ inputField .setText (inputPromptMessage );
143
+ });
143
144
144
- inputField .addKeyListener (new KeyAdapter () {
145
- @ Override
146
- public void keyPressed (final KeyEvent e ) {
147
- // If this is the first character being typed
148
- boolean backspace = (e .keyCode == SWT .DEL || e .keyCode == SWT .BS );
149
- if (inputField .getText ().equals (inputPromptMessage )) {
150
- if (!backspace ) {
151
- inputField .setText ("" );
152
- inputField .setForeground (isDarkTheme
153
- ? Display .getDefault ().getSystemColor (SWT .COLOR_WHITE )
154
- : Display .getDefault ().getSystemColor (SWT .COLOR_BLACK ));
155
- }
156
- e .doit = !backspace ;
157
- } else if (backspace && inputField .getText ().length () <= 1 ) {
158
- inputField .setText (inputPromptMessage );
159
- inputField .setForeground (Display .getDefault ().getSystemColor (SWT .COLOR_GRAY ));
145
+ inputField .addKeyListener (new KeyAdapter () {
146
+ @ Override
147
+ public void keyPressed (final KeyEvent e ) {
148
+ // If this is the first character being typed
149
+ boolean backspace = (e .keyCode == SWT .DEL || e .keyCode == SWT .BS );
150
+ if (inputField .getText ().equals (inputPromptMessage )) {
151
+ if (!backspace ) {
152
+ inputField .setText ("" );
153
+ inputField .setForeground (isDarkTheme
154
+ ? Display .getDefault ().getSystemColor (SWT .COLOR_WHITE )
155
+ : Display .getDefault ().getSystemColor (SWT .COLOR_BLACK ));
160
156
}
157
+ e .doit = !backspace ;
158
+ } else if (backspace && inputField .getText ().length () <= 1 ) {
159
+ inputField .setText (inputPromptMessage );
160
+ inputField .setForeground (Display .getDefault ().getSystemColor (SWT .COLOR_GRAY ));
161
161
}
162
- });
163
- } else {
164
- inputField .setMessage (inputPromptMessage );
165
- }
162
+ }
163
+ });
166
164
167
165
GridData gridData = new GridData (GridData .FILL_HORIZONTAL );
168
166
gridData .widthHint = 350 ;
167
+ gridData .heightHint = 25 ;
169
168
inputField .setLayoutData (gridData );
170
169
171
- // Enforce maximum character count that can be entered into the input
172
- inputField .addVerifyListener (e -> {
173
- String currentText = inputField .getText ();
174
- String newText = currentText .substring (0 , e .start ) + e .text + currentText .substring (e .end );
175
- if (newText .length () > maxInputLength ) {
176
- e .doit = false ; // Prevent the input
177
- }
178
- });
179
-
180
170
inputField .addKeyListener (new KeyAdapter () {
181
171
@ Override
182
172
public void keyPressed (final KeyEvent e ) {
183
- if (e .character == SWT .CR || e .character == SWT .LF ) {
173
+ if (e .keyCode == SWT .CR || e .keyCode == SWT .LF ) {
184
174
// Gather inputs and send back to controller
185
175
var userInput = inputField .getText ();
186
176
if (userInputIsValid (userInput )) {
@@ -235,14 +225,51 @@ private void showPrompt(final String promptText) {
235
225
latestOffset = task .getSelectionOffset ();
236
226
}
237
227
currentPaintListener = createPaintListenerPrompt (widget , latestOffset , promptText , isDarkTheme );
238
- widget . addPaintListener ( currentPaintListener );
228
+ addPaintListenerAndCapture ( widget , currentPaintListener );
239
229
widget .redraw ();
240
230
} catch (Exception e ) {
241
231
Activator .getLogger ().error ("Failed to create paint listener: " + e .getMessage (), e );
242
232
}
243
233
});
244
234
}
245
235
236
+ private void addPaintListenerAndCapture (final StyledText widget , final PaintListener paintListener ) {
237
+ var listenersBefore = new HashSet <>(Arrays .asList (widget .getListeners (SWT .Paint )));
238
+ widget .addPaintListener (paintListener );
239
+ var listenersAfter = widget .getListeners (SWT .Paint );
240
+ for (var listener : listenersAfter ) {
241
+ if (!listenersBefore .contains (listener )) {
242
+ if (isAdtPaintListener (listener )) {
243
+ paintListenerRef = listener ;
244
+ }
245
+ break ;
246
+ }
247
+ }
248
+ }
249
+
250
+ /**
251
+ * ADT Viewers wrap the paint listener into an internal delegate(PaintListenerDelegate).
252
+ * which needs to be captured to effectively remove it later
253
+ * @param listener
254
+ * @return listener ref
255
+ */
256
+ private boolean isAdtPaintListener (final Listener listener ) {
257
+ try {
258
+ // Use reflection to check if this wraps a PaintListenerDelegate
259
+ if (listener .getClass ().getName ().contains ("TypedListener" )) {
260
+ var eventListenerField = listener .getClass ().getDeclaredField ("eventListener" );
261
+ eventListenerField .setAccessible (true );
262
+ Object eventListener = eventListenerField .get (listener );
263
+ if (eventListener != null && eventListener .getClass ().getName ().contains ("PaintListenerDelegate" )) {
264
+ return true ;
265
+ }
266
+ }
267
+ } catch (Exception e ) {
268
+ // Ignore reflection errors
269
+ }
270
+ return false ;
271
+ }
272
+
246
273
public void updatePromptPosition (final SessionState state ) {
247
274
try {
248
275
int offset = ((ITextViewerExtension5 ) viewer ).modelOffset2WidgetOffset (task .getSelectionOffset ());
@@ -340,9 +367,17 @@ private void removeCurrentPaintListener() {
340
367
return ;
341
368
}
342
369
try {
343
- if (viewer .getTextWidget () != null && !viewer .getTextWidget ().isDisposed () && currentPaintListener != null ) {
344
- viewer .getTextWidget ().removePaintListener (currentPaintListener );
345
- viewer .getTextWidget ().redraw ();
370
+ var widget = viewer .getTextWidget ();
371
+ if (widget != null && !widget .isDisposed () && currentPaintListener != null ) {
372
+ // remove adt specific paint listener if present
373
+ if (paintListenerRef != null ) {
374
+ widget .removeListener (SWT .Paint , paintListenerRef );
375
+ paintListenerRef = null ;
376
+ }
377
+ if (currentPaintListener != null ) {
378
+ widget .removePaintListener (currentPaintListener );
379
+ }
380
+ widget .redraw ();
346
381
currentPaintListener = null ;
347
382
}
348
383
} catch (Exception e ) {
@@ -364,7 +399,7 @@ private int calculateIndentOffset(final StyledText widget, final int currentOffs
364
399
}
365
400
366
401
private boolean userInputIsValid (final String input ) {
367
- return input != null && input .length () >= 2 && input . length () < maxInputLength ;
402
+ return input != null && input .length () >= 2 ;
368
403
}
369
404
370
405
/**
0 commit comments