Skip to content
Open
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
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* Copyright (c) 2000, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -25,6 +25,7 @@
import org.eclipse.e4.ui.model.application.ui.MUILabel;
import org.eclipse.e4.ui.model.application.ui.advanced.MPlaceholder;
import org.eclipse.e4.ui.model.application.ui.basic.MPart;
import org.eclipse.e4.ui.workbench.IPresentationEngine;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.e4.ui.workbench.renderers.swt.CTabRendering;
import org.eclipse.e4.ui.workbench.renderers.swt.StackRenderer;
Expand Down Expand Up @@ -126,8 +127,16 @@ public BasicPartList(Shell parent, int shellStyle, int treeStyler,
@Override
public int category(Object element) {
if (element instanceof MPart part) {
boolean pinned = isPinned(part);
CTabItem item = BasicPartList.this.renderer.findItemForPart(part);
if (item != null && !item.isShowing()) {
boolean notShowing = item != null && !item.isShowing();
if (shouldShowPinnedEditorsFirst()) {
if (pinned) {
return notShowing ? -3 : -2;
}
return notShowing ? -1 : 0;
}
if (notShowing) {
return -1;
}
}
Expand Down Expand Up @@ -170,9 +179,37 @@ private List<Object> getInput() {
list.add(element);
}
}
if (shouldShowPinnedEditorsFirst()) {
List<Object> pinned = new ArrayList<>();
List<Object> unpinned = new ArrayList<>();
for (Object element : list) {
if (element instanceof MPart part && isPinned(part)) {
pinned.add(part);
} else {
unpinned.add(element);
}
}
pinned.addAll(unpinned);
return pinned;
}
return list;
}

private boolean isPinned(MPart part) {
return part.getTags().contains(IPresentationEngine.ADORNMENT_PIN);
}

private boolean shouldShowPinnedEditorsFirst() {
if (!PartStackUtil.isEditorStack(input)) {
return false;
}
return Platform.getPreferencesService().getBoolean(
CTabRendering.PREF_QUALIFIER_ECLIPSE_E4_UI_WORKBENCH_RENDERERS_SWT,
CTabRendering.SHOW_PINNED_EDITORS_FIRST,
CTabRendering.SHOW_PINNED_EDITORS_FIRST_DEFAULT,
null);
}

public void setInput() {
getTableViewer().setInput(getInput());
selectFirstMatch();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2010, 2020 IBM Corporation and others.
* Copyright (c) 2010, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -91,6 +91,16 @@ public class CTabRendering extends CTabFolderRenderer implements ICTabRendering,
*/
public static final boolean SHOW_DIRTY_INDICATOR_ON_TABS_DEFAULT = true;

/**
* A named preference for whether to show pinned editors first in the drop-down list
*/
public static final String SHOW_PINNED_EDITORS_FIRST = "SHOW_PINNED_EDITORS_FIRST"; //$NON-NLS-1$

/**
* Default value for "show pinned editors first" preference
*/
public static final boolean SHOW_PINNED_EDITORS_FIRST_DEFAULT = false;

private static int MIN_VIEW_CHARS = 1;
private static int MAX_VIEW_CHARS = Integer.MAX_VALUE;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2008, 2025 IBM Corporation and others.
* Copyright (c) 2008, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand All @@ -26,6 +26,7 @@
import jakarta.inject.Named;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
Expand Down Expand Up @@ -72,6 +73,8 @@
import org.eclipse.e4.ui.workbench.modeling.EModelService;
import org.eclipse.e4.ui.workbench.modeling.EPartService;
import org.eclipse.e4.ui.workbench.modeling.ISaveHandler;
import org.eclipse.emf.common.util.ECollections;
import org.eclipse.emf.common.util.EList;
import org.eclipse.jface.action.IContributionItem;
import org.eclipse.jface.action.LegacyActionTools;
import org.eclipse.jface.action.MenuManager;
Expand Down Expand Up @@ -182,6 +185,8 @@ public class StackRenderer extends LazyStackRenderer {
* tabs using the keyboard.
*/
private static final String INHIBIT_FOCUS = "InhibitFocus"; //$NON-NLS-1$
private static final String PART_OPEN_INDEX = "part_open_index"; //$NON-NLS-1$
private static final java.util.concurrent.atomic.AtomicLong PART_SEQUENCE_GEN = new java.util.concurrent.atomic.AtomicLong();

// Minimum characters in for stacks outside the shared area
private static int MIN_VIEW_CHARS = 1;
Expand Down Expand Up @@ -385,14 +390,115 @@ void subscribeTopicTagsChanged(@UIEventTopic(UIEvents.ApplicationElement.TOPIC_T
if (UIEvents.isADD(event)) {
if (UIEvents.contains(event, UIEvents.EventTags.NEW_VALUE, IPresentationEngine.ADORNMENT_PIN)) {
item.setImage(getImage(part));
handlePartPinStateChanged(part, true);
}
} else if (UIEvents.isREMOVE(event)) {
if (UIEvents.contains(event, UIEvents.EventTags.OLD_VALUE, IPresentationEngine.ADORNMENT_PIN)) {
item.setImage(getImage(part));
handlePartPinStateChanged(part, false);
}
}
}

private static long getPartOpenSequence(MStackElement element) {
MPart part = element instanceof MPart p ? p : (MPart) ((MPlaceholder) element).getRef();
if (part != null) {
Object seq = part.getTransientData().get(PART_OPEN_INDEX);
if (seq instanceof Long l) {
return l;
}
}
return 0L;
}

private static void ensurePartOpenSequence(MStackElement element) {
MPart part = element instanceof MPart p ? p : (MPart) ((MPlaceholder) element).getRef();
if (part != null && !part.getTransientData().containsKey(PART_OPEN_INDEX)) {
part.getTransientData().put(PART_OPEN_INDEX, PART_SEQUENCE_GEN.incrementAndGet());
}
}

private void handlePartPinStateChanged(MPart part, boolean pinned) {
if (!shouldShowPinnedEditorsFirst()) {
return;
}
MUIElement parent = part.getParent();
if (parent == null && part.getCurSharedRef() != null) {
parent = part.getCurSharedRef().getParent();
}
if (!(parent instanceof MPartStack stack) || !PartStackUtil.isEditorStack(stack)) {
return;
}
MUIElement elementToMove = part.getCurSharedRef() != null ? part.getCurSharedRef() : part;
List<MStackElement> children = stack.getChildren();
int currentIndex = children.indexOf(elementToMove);
if (currentIndex < 0) {
return;
}
for (MStackElement child : children) {
ensurePartOpenSequence(child);
}
long elementSeq = getPartOpenSequence((MStackElement) elementToMove);
int targetIndex;
if (pinned) {
// Find position among pinned items sorted by open order
int insertIndex = 0;
for (int i = 0; i < children.size(); i++) {
MStackElement child = children.get(i);
if (child == elementToMove) {
continue;
}
MPart childPart = child instanceof MPart p ? p : (MPart) ((MPlaceholder) child).getRef();
if (childPart != null && isPinned(childPart)) {
if (getPartOpenSequence(child) < elementSeq) {
insertIndex++;
}
}
}
targetIndex = insertIndex;
} else {
// Find position among unpinned items (after all pinned items) sorted by open order
int pinnedCount = 0;
int unpinnedBeforeCount = 0;
for (MStackElement child : children) {
if (child == elementToMove) {
continue;
}
MPart childPart = child instanceof MPart p ? p : (MPart) ((MPlaceholder) child).getRef();
if (childPart != null && isPinned(childPart)) {
pinnedCount++;
} else if (getPartOpenSequence(child) < elementSeq) {
unpinnedBeforeCount++;
}
}
targetIndex = pinnedCount + unpinnedBeforeCount;
}
if (currentIndex != targetIndex) {
if (children instanceof EList<MStackElement> eList) {
ECollections.move(eList, targetIndex, currentIndex);
} else {
children.remove(currentIndex);
if (targetIndex >= children.size()) {
children.add((MStackElement) elementToMove);
} else {
children.add(targetIndex, (MStackElement) elementToMove);
}
}
}
}

private boolean isPinned(MPart part) {
return part.getTags().contains(IPresentationEngine.ADORNMENT_PIN);
}

private boolean shouldShowPinnedEditorsFirst() {
return Platform.getPreferencesService().getBoolean(
CTabRendering.PREF_QUALIFIER_ECLIPSE_E4_UI_WORKBENCH_RENDERERS_SWT,
CTabRendering.SHOW_PINNED_EDITORS_FIRST,
CTabRendering.SHOW_PINNED_EDITORS_FIRST_DEFAULT,
null);
}

@Inject
@Optional
void subscribeTopicChildrenChanged(@UIEventTopic(UIEvents.ElementContainer.TOPIC_CHILDREN) Event event) {
Expand Down Expand Up @@ -716,6 +822,8 @@ public void init() {
dirtyIndicatorListener = e -> {
if (CTabRendering.SHOW_DIRTY_INDICATOR_ON_TABS.equals(e.getKey())) {
synchronize.asyncExec(this::updateDirtyIndicatorStyle);
} else if (CTabRendering.SHOW_PINNED_EDITORS_FIRST.equals(e.getKey())) {
synchronize.asyncExec(this::reorderStacksForPinnedEditors);
}
};
preferences.addPreferenceChangeListener(dirtyIndicatorListener);
Expand Down Expand Up @@ -748,6 +856,57 @@ private void updateDirtyIndicatorStyle() {
}
}

private void reorderStacksForPinnedEditors() {
MApplication app = context.get(MApplication.class);
if (app == null) {
return;
}
boolean showPinnedFirst = shouldShowPinnedEditorsFirst();
List<MPartStack> stacks = modelService.findElements(app, null, MPartStack.class, null);
for (MPartStack stack : stacks) {
if (!PartStackUtil.isEditorStack(stack)) {
continue;
}
List<MStackElement> children = stack.getChildren();
for (MStackElement child : children) {
ensurePartOpenSequence(child);
}
List<MStackElement> desiredOrder = new ArrayList<>(children);
if (showPinnedFirst) {
List<MStackElement> pinned = new ArrayList<>();
List<MStackElement> unpinned = new ArrayList<>();
for (MStackElement child : children) {
MPart part = child instanceof MPart p ? p : (MPart) ((MPlaceholder) child).getRef();
if (part != null && isPinned(part)) {
pinned.add(child);
} else {
unpinned.add(child);
}
}
pinned.sort(Comparator.comparingLong(StackRenderer::getPartOpenSequence));
unpinned.sort(Comparator.comparingLong(StackRenderer::getPartOpenSequence));
desiredOrder.clear();
desiredOrder.addAll(pinned);
desiredOrder.addAll(unpinned);
} else {
desiredOrder.sort(Comparator.comparingLong(StackRenderer::getPartOpenSequence));
}

for (int i = 0; i < desiredOrder.size(); i++) {
MStackElement elem = desiredOrder.get(i);
int cur = children.indexOf(elem);
if (cur != i) {
if (children instanceof EList<MStackElement> eList) {
ECollections.move(eList, i, cur);
} else {
children.remove(cur);
children.add(i, elem);
}
}
}
}
}

protected void updateTab(CTabItem cti, MPart part, String attName, Object newValue) {
switch (attName) {
case UIEvents.UILabel.LABEL:
Expand Down Expand Up @@ -1150,10 +1309,14 @@ private int calcIndexFor(MElementContainer<? extends MUIElement> stack, final MU
public void childRendered(final MElementContainer<MUIElement> parentElement, MUIElement element) {
super.childRendered(parentElement, element);

if (!(((MUIElement) parentElement) instanceof MPartStack) || !(element instanceof MStackElement)) {
if (!(((MUIElement) parentElement) instanceof MPartStack stack) || !(element instanceof MStackElement stackElement)) {
return;
}

if (PartStackUtil.isEditorStack(stack)) {
ensurePartOpenSequence(stackElement);
}

createTab(parentElement, element);
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2000, 2015 IBM Corporation and others.
* Copyright (c) 2000, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -61,6 +61,7 @@ protected Control createContents(Composite parent) {
createAllowInplaceEditorPref(composite);
createUseIPersistablePref(composite);
createPromptWhenStillOpenPref(composite);
createShowPinnedEditorsFirstPref(composite);
createEditorReuseGroup(composite);
createAlignMultiPageEditorTabs(composite);

Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2005, 2020 IBM Corporation and others.
* Copyright (c) 2005, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -472,6 +472,7 @@ public class WorkbenchMessages extends NLS {
public static String WorkbenchPreference_allowInplaceEditingButton;
public static String WorkbenchPreference_useIPersistableEditorButton;
public static String WorkbenchPreference_promptWhenStillOpenButton;
public static String WorkbenchPreference_showPinnedEditorsFirstButton;
public static String WorkbenchPreference_stickyCycleButton;
public static String WorkbenchPreference_RunInBackgroundButton;
public static String WorkbenchPreference_RunInBackgroundToolTip;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*******************************************************************************
* Copyright (c) 2007, 2019 IBM Corporation and others.
* Copyright (c) 2007, 2026 IBM Corporation and others.
*
* This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
Expand Down Expand Up @@ -98,6 +98,13 @@ private static boolean isMruEnabled() {
return preferences.getBoolean(StackRenderer.MRU_KEY, initialMRUValue);
}

private static boolean isShowPinnedEditorsFirstEnabled() {
IEclipsePreferences preferences = InstanceScope.INSTANCE
.getNode(CTabRendering.PREF_QUALIFIER_ECLIPSE_E4_UI_WORKBENCH_RENDERERS_SWT);
return preferences.getBoolean(CTabRendering.SHOW_PINNED_EDITORS_FIRST,
CTabRendering.SHOW_PINNED_EDITORS_FIRST_DEFAULT);
}

@Override
protected Object getInput(WorkbenchPage page) {
List<EditorReference> editorReferences = getParts(page);
Expand All @@ -117,6 +124,19 @@ private List<EditorReference> getParts(WorkbenchPage page) {
refs.add((EditorReference) ier);
}
}
if (isShowPinnedEditorsFirstEnabled()) {
List<EditorReference> pinned = new ArrayList<>();
List<EditorReference> unpinned = new ArrayList<>();
for (EditorReference ref : refs) {
if (ref.isPinned()) {
pinned.add(ref);
} else {
unpinned.add(ref);
}
}
pinned.addAll(unpinned);
return pinned;
}
return refs;
}

Expand Down
Loading
Loading