1414package org .eclipse .ui .internal .handlers ;
1515
1616import java .lang .reflect .Method ;
17+ import java .util .stream .Stream ;
1718import org .eclipse .core .commands .ExecutionEvent ;
1819import org .eclipse .swt .SWT ;
20+ import org .eclipse .swt .custom .CTabFolder ;
21+ import org .eclipse .swt .custom .CTabItem ;
1922import org .eclipse .swt .widgets .Control ;
2023import org .eclipse .swt .widgets .Display ;
2124import org .eclipse .swt .widgets .Shell ;
@@ -38,20 +41,86 @@ public class TraversePageHandler extends WidgetMethodHandler {
3841 public final Object execute (final ExecutionEvent event ) {
3942 Control focusControl = Display .getCurrent ().getFocusControl ();
4043 if (focusControl != null ) {
44+ boolean forward = "next" .equals (methodName ); //$NON-NLS-1$
4145 int traversal = "next" .equals (methodName ) ? SWT .TRAVERSE_PAGE_NEXT : SWT .TRAVERSE_PAGE_PREVIOUS ; //$NON-NLS-1$
4246 Control control = focusControl ;
4347 do {
44- if (control .traverse (traversal ))
48+ if (control instanceof CTabFolder folder && isFinalItemInCTabFolder (folder , forward )
49+ && !areHiddenItems (folder )) {
50+
51+ loopToSecondToFirstItemInCTabFolder (folder , forward );
52+ traversal = getTraversalDirection (!forward ); // we are in the second-to-last item in the given
53+ // direction. Now, use the Traverse-event to move back by one
54+ }
55+ if (control .traverse (traversal )) {
4556 return null ;
46- if (control instanceof Shell )
57+ }
58+ if (control instanceof Shell ) {
4759 return null ;
60+ }
4861 control = control .getParent ();
4962 } while (control != null );
5063 }
5164
5265 return null ;
5366 }
5467
68+
69+ private int getTraversalDirection (boolean direction ) {
70+ return direction ? SWT .TRAVERSE_PAGE_NEXT : SWT .TRAVERSE_PAGE_PREVIOUS ;
71+ }
72+
73+ /**
74+ * Sets the current selection to the second-to-last item in the given direction.
75+ *
76+ * @param folder the CTabFolder which we want to inspect
77+ * @param forward whether we want to traverse forwards of backwards
78+ */
79+ private void loopToSecondToFirstItemInCTabFolder (CTabFolder folder , boolean forward ) {
80+ if (forward ) {
81+ folder .showItem (folder .getItem (0 ));
82+ folder .setSelection (1 );
83+ } else {
84+ int itemCount = folder .getItemCount ();
85+ folder .setSelection (itemCount - 2 );
86+ }
87+ }
88+
89+ /**
90+ * {@return Returns whether the folder has currently selected the final item in
91+ * the given direction.}
92+ *
93+ * @param folder the CTabFolder which we want to inspect
94+ * @param forward whether we want to traverse forwards of backwards
95+ */
96+ private boolean isFinalItemInCTabFolder (CTabFolder folder , boolean forward ) {
97+ folder .update ();
98+
99+ CTabItem currentFolder = folder .getSelection ();
100+ CTabItem lastFolder = null ;
101+ if (forward ) {
102+ int itemCount = folder .getItemCount ();
103+
104+ lastFolder = folder .getItem (itemCount - 1 );
105+
106+ } else {
107+ lastFolder = folder .getItem (0 );
108+ }
109+ return currentFolder .equals (lastFolder );
110+ }
111+
112+ /**
113+ * checks if there are any tabs which are not currently shown in the top bar
114+ *
115+ * @param folder the tabs which are currently opened
116+ * @return if there are any items which are not shown in the folder (tabs in the
117+ * chevron)
118+ */
119+ private boolean areHiddenItems (CTabFolder folder ) {
120+ CTabItem [] items = folder .getItems ();
121+ return Stream .of (items ).anyMatch (i -> i .isShowing ());
122+ }
123+
55124 /**
56125 * Looks up the traverse(int) method on the given focus control.
57126 *
0 commit comments