Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,15 @@ Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: %pluginName
Bundle-SymbolicName: org.eclipse.terminal.view.ui;singleton:=true
Bundle-Version: 1.1.100.qualifier
Bundle-Version: 1.1.200.qualifier
Bundle-Activator: org.eclipse.terminal.view.ui.internal.UIPlugin
Bundle-Vendor: %providerName
Require-Bundle: org.eclipse.core.expressions;bundle-version="[3.9.0,4.0.0)",
org.eclipse.core.runtime;bundle-version="[3.33.0,4)",
org.eclipse.core.resources;bundle-version="[3.22.0,4.0.0)";resolution:=optional,
org.eclipse.core.variables;bundle-version="[3.6.0,4.0.0)",
org.eclipse.debug.ui;bundle-version="[3.18.0,4.0.0)";resolution:=optional,
org.eclipse.swt;bundle-version="[3.135.0,4.0.0)",
org.eclipse.ui;bundle-version="[3.208.0,4.0.0)",
org.eclipse.ui.ide;bundle-version="[3.22.0,4.0.0)";resolution:=optional,
org.eclipse.ui.editors;bundle-version="[3.20.0,4.0.0)";resolution:=optional,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -261,12 +261,9 @@ private void addDropSupport() {
target.addDropListener(new DropTargetListener() {
@Override
public void dragEnter(DropTargetEvent event) {
// only if the drop target is different then the drag source
if (TerminalTransfer.getInstance().getTabFolderManager() == tabFolderManager) {
event.detail = DND.DROP_NONE;
} else {
event.detail = DND.DROP_MOVE;
}
// Accept the move both for a different terminals view (the terminal is moved
// to the other view) and for the same view (the tab is reordered).
event.detail = DND.DROP_MOVE;
}

@Override
Expand All @@ -290,6 +287,12 @@ public void drop(DropTargetEvent event) {
if (TerminalTransfer.getInstance().getDraggedFolderItem() != null && tabFolderManager != null) {
CTabItem draggedItem = TerminalTransfer.getInstance().getDraggedFolderItem();

// Drop within the same terminals view: reorder the dragged tab in place.
if (TerminalTransfer.getInstance().getTabFolderManager() == tabFolderManager) {
reorderTabItem(draggedItem, event.x, event.y);
return;
}

CTabItem item = tabFolderManager.cloneTabItemAfterDrop(draggedItem);
tabFolderManager.bringToTop(item);
switchToTabFolderControl();
Expand All @@ -313,6 +316,61 @@ public void drop(DropTargetEvent event) {
});
}

/**
* Reorder the dragged tab item within its own tab folder so that it is dropped at the position
* the mouse points to.
*
* @param draggedItem the tab item being dragged, must not be <code>null</code>.
* @param x the x coordinate of the drop, in display-relative coordinates.
* @param y the y coordinate of the drop, in display-relative coordinates.
*/
private void reorderTabItem(CTabItem draggedItem, int x, int y) {
if (tabFolderControl == null || tabFolderControl.isDisposed()) {
return;
}

int from = tabFolderControl.indexOf(draggedItem);
if (from == -1) {
return;
}

// Map the display-relative drop coordinates to the tab folder and find the tab below them.
// A drop next to the tabs (e.g. on the trailing empty space) targets the last position.
Point point = tabFolderControl.toControl(x, y);
CTabItem targetItem = tabFolderControl.getItem(point);
int indexUnderCursor = targetItem != null ? tabFolderControl.indexOf(targetItem) : -1;

int to = computeReorderIndex(from, indexUnderCursor, tabFolderControl.getItemCount());
if (to != -1) {
tabFolderControl.moveItem(from, to);
}

// Keep the moved terminal selected and focused.
tabFolderManager.bringToTop(draggedItem);
setFocus();
}

/**
* Computes the destination index for a tab reorder triggered by a drop.
* <p>
* This method is internal and only exposed for testing.
* </p>
*
* @param from the current index of the dragged tab.
* @param indexUnderCursor the index of the tab below the drop location, or <code>-1</code> if the
* drop did not happen over a tab (for example on the empty space following the last tab).
* @param itemCount the total number of tabs in the folder.
* @return the index the dragged tab should be moved to, or <code>-1</code> if no move is required
* because the tab would keep its position.
*/
public static int computeReorderIndex(int from, int indexUnderCursor, int itemCount) {
int to = indexUnderCursor != -1 ? indexUnderCursor : itemCount - 1;
if (to < 0 || to == from) {
return -1;
}
return to;
}

@Override
public void dispose() {
// Dispose the tab folder manager
Expand Down
2 changes: 1 addition & 1 deletion terminal/features/org.eclipse.terminal.feature/feature.xml
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
<feature
id="org.eclipse.terminal.feature"
label="%featureName"
version="1.0.300.qualifier"
version="1.0.400.qualifier"
provider-name="%providerName"
license-feature="org.eclipse.license"
license-feature-version="0.0.0">
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ Bundle-Localization: plugin
Require-Bundle: org.eclipse.core.runtime;bundle-version="[3.33.0,4)",
org.eclipse.ui;bundle-version="[3.208.0,4)",
org.opentest4j;bundle-version="[1.3.0,2)",
org.eclipse.terminal.control;bundle-version="1.0.0"
org.eclipse.terminal.control;bundle-version="1.0.0",
org.eclipse.terminal.view.ui;bundle-version="[1.1.200,2.0.0)"
Bundle-RequiredExecutionEnvironment: JavaSE-21
Export-Package: org.eclipse.terminal.internal.connector;x-internal:=true,
org.eclipse.terminal.internal.emulator;x-internal:=true,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
org.eclipse.terminal.model.AllTestSuite.class, //
org.eclipse.terminal.internal.connector.TerminalConnectorTest.class, //
org.eclipse.terminal.internal.connector.TerminalToRemoteInjectionOutputStreamTest.class, //
org.eclipse.terminal.view.ui.tests.TerminalsViewReorderTest.class, //
})
public class AutomatedTestSuite {

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
/*******************************************************************************
* Copyright (c) 2026 Eclipse contributors and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.terminal.view.ui.tests;

import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertSame;

import org.eclipse.swt.SWT;
import org.eclipse.swt.custom.CTabFolder;
import org.eclipse.swt.custom.CTabItem;
import org.eclipse.swt.widgets.Display;
import org.eclipse.swt.widgets.Shell;
import org.eclipse.terminal.view.ui.internal.view.TerminalsView;
import org.junit.jupiter.api.AfterAll;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;

/**
* Tests for reordering terminal tabs in the terminals view (see
* <a href="https://github.com/eclipse-platform/eclipse.platform/issues/2679">issue 2679</a>).
*/
public class TerminalsViewReorderTest {

private static Display display = null;

@BeforeAll
public static void createDisplay() {
if (Display.getCurrent() == null) {
display = new Display();
}
}

@AfterAll
public static void disposeDisplay() {
if (display != null) {
display.dispose();
display = null;
}
}

@Test
public void dropOverAnotherTabTargetsThatTab() {
assertEquals(2, TerminalsView.computeReorderIndex(0, 2, 4));
}

@Test
public void dropOverItselfIsNoOp() {
assertEquals(-1, TerminalsView.computeReorderIndex(2, 2, 4));
}

@Test
public void dropNextToTheTabsTargetsTheLastPosition() {
assertEquals(3, TerminalsView.computeReorderIndex(1, -1, 4));
}

@Test
public void dropNextToTheTabsWhileAlreadyLastIsNoOp() {
assertEquals(-1, TerminalsView.computeReorderIndex(3, -1, 4));
}

@Test
public void singleTabIsNeverReordered() {
assertEquals(-1, TerminalsView.computeReorderIndex(0, -1, 1));
}

/**
* Verifies the {@link CTabFolder#moveItem(int, int)} contract the reorder feature relies on:
* the items are reordered and the previously selected item stays selected.
*/
@Test
public void moveItemReordersAndKeepsSelection() {
Shell shell = new Shell(display);
try {
CTabFolder folder = new CTabFolder(shell, SWT.NONE);
CTabItem a = newItem(folder, "A");
CTabItem b = newItem(folder, "B");
newItem(folder, "C");
newItem(folder, "D");

folder.setSelection(b);

// Move "A" (index 0) to position 2: expected order is B, C, A, D.
folder.moveItem(0, 2);

assertEquals("B", folder.getItem(0).getText());
assertEquals("C", folder.getItem(1).getText());
assertEquals("A", folder.getItem(2).getText());
assertEquals("D", folder.getItem(3).getText());
assertEquals(2, folder.indexOf(a));

// The selected item is unchanged even though its index moved.
assertSame(b, folder.getSelection());
} finally {
shell.dispose();
}
}

private static CTabItem newItem(CTabFolder folder, String text) {
CTabItem item = new CTabItem(folder, SWT.CLOSE);
item.setText(text);
return item;
}
}
Loading