Skip to content

Commit f2c2a90

Browse files
committed
fix: refresh scroller layout when tool confirmation dialog is cancelled
When the user cancels a subagent tool confirmation dialog, the subagent panel stays expanded at its pre-cancel height. This happens because cancelConfirmation() only called parent.layout() (relaying the turn widget's children), but never updated the ScrolledComposite's minimum size via refreshScrollerLayout(). On the Continue path this goes unnoticed because subsequent subagent messages trigger a layout refresh; on the Cancel path no further messages arrive, so the panel is stuck. Fix by adding a dispose listener on the dialog in BaseTurnWidget that walks up to the nearest ChatContentViewer and calls requestRefreshScrollerLayout(), a new coalesced async helper that schedules a single refreshScrollerLayout() after pending SWT mutations settle. InvokeToolConfirmationDialog remains self-contained: both the accept and cancel paths use a private disposeAndRequestParentLayout() helper, keeping scroller-specific behaviour out of the dialog. Fixes #169
1 parent 82643d2 commit f2c2a90

3 files changed

Lines changed: 24 additions & 18 deletions

File tree

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/BaseTurnWidget.java

Lines changed: 10 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -649,17 +649,17 @@ public CompletableFuture<LanguageModelToolConfirmationResult> requestToolExecuti
649649
ConfirmationContent content, Object input) {
650650
reset();
651651

652-
Runnable refreshLayout = null;
653-
Composite ancestor = this.getParent();
654-
while (ancestor != null && !ancestor.isDisposed()) {
655-
if (ancestor instanceof ChatContentViewer) {
656-
final ChatContentViewer viewer = (ChatContentViewer) ancestor;
657-
refreshLayout = viewer::refreshScrollerLayout;
658-
break;
652+
this.confirmDialog = new InvokeToolConfirmationDialog(this, content, input);
653+
this.confirmDialog.addDisposeListener(e -> {
654+
Composite ancestor = this.getParent();
655+
while (ancestor != null && !ancestor.isDisposed()) {
656+
if (ancestor instanceof ChatContentViewer) {
657+
((ChatContentViewer) ancestor).requestRefreshScrollerLayout();
658+
break;
659+
}
660+
ancestor = ancestor.getParent();
659661
}
660-
ancestor = ancestor.getParent();
661-
}
662-
this.confirmDialog = new InvokeToolConfirmationDialog(this, content, input, refreshLayout);
662+
});
663663
CompletableFuture<LanguageModelToolConfirmationResult> toolConfirmationFuture = this.confirmDialog
664664
.getConfirmationFuture();
665665

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/ChatContentViewer.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -404,6 +404,14 @@ public void renderErrorMessage(String errorMessage) {
404404
scrollToLatestUserTurn();
405405
}
406406

407+
/**
408+
* Schedules a single async {@link #refreshScrollerLayout()} call so that multiple dispose/layout
409+
* events that arrive in the same event-loop tick are coalesced into one pass.
410+
*/
411+
public void requestRefreshScrollerLayout() {
412+
SwtUtils.invokeOnDisplayThreadAsync(() -> refreshScrollerLayout(), this);
413+
}
414+
407415
/**
408416
* Update the size of scrolled composite when there are content updates.
409417
*/

com.microsoft.copilot.eclipse.ui/src/com/microsoft/copilot/eclipse/ui/chat/InvokeToolConfirmationDialog.java

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -68,22 +68,19 @@ public class InvokeToolConfirmationDialog extends Composite {
6868
private Runnable titleFontChangeCallback;
6969
private ConfirmationContent confirmationContent;
7070
private ConfirmationAction selectedAction;
71-
private final Runnable onCancelCallback;
7271

7372
/**
7473
* Create a new confirmation dialog driven by {@link ConfirmationContent}.
7574
*
7675
* @param parent the parent composite
7776
* @param content confirmation content with title, message, and action buttons
7877
* @param input the input object to pass to the tool
79-
* @param onCancelCallback invoked after the dialog is disposed on cancellation, for layout refresh
8078
*/
8179
public InvokeToolConfirmationDialog(Composite parent,
82-
ConfirmationContent content, Object input, Runnable onCancelCallback) {
80+
ConfirmationContent content, Object input) {
8381
super(parent, SWT.BORDER | SWT.WRAP);
8482
this.setLayout(new GridLayout(1, false));
8583
this.setLayoutData(new GridData(SWT.FILL, SWT.FILL, true, false));
86-
this.onCancelCallback = onCancelCallback;
8784

8885
this.confirmationContent = content;
8986
createDialogContent(content.getTitle(), content.getMessage(), input);
@@ -122,10 +119,7 @@ public void cancelConfirmation() {
122119
&& StringUtils.isNotEmpty(this.cancelMessage)) {
123120
new AgentToolCancelLabel(parent, SWT.NONE, this.cancelMessage);
124121
}
125-
this.dispose();
126-
if (onCancelCallback != null) {
127-
onCancelCallback.run();
128-
}
122+
disposeAndRequestParentLayout();
129123
}, this);
130124
}
131125
}
@@ -321,6 +315,10 @@ private void acceptAndDispose(ConfirmationAction action) {
321315
new LanguageModelToolConfirmationResult(
322316
ToolConfirmationResult.ACCEPT));
323317

318+
disposeAndRequestParentLayout();
319+
}
320+
321+
private void disposeAndRequestParentLayout() {
324322
Composite parent = this.getParent();
325323
this.dispose();
326324
if (parent != null && !parent.isDisposed()) {

0 commit comments

Comments
 (0)