Repository: dbeaver/dbeaver
Pull request #37688: dbeaver/pro#4300 Breadcrumb positioning; use context menu as drop-down
Author: login: ShadelessFox
Created: 2025-03-31T16:13:04Z | Merged: 2025-04-01T17:02:41Z

Configurable here:
![java_Iui89zzzUp](https://github.com/user-attachments/assets/2880fd89-01a3-4dad-ae3b-56a63a5b7dd3)

How it looks in editors:
![java_EX6Cl8cj4w](https://github.com/user-attachments/assets/ae58c899-8832-47ca-a1bc-ddb33ee70901)



Diff:
diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/DBeaverPreferences.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/DBeaverPreferences.java
index be96166b759e..ee98418a0cbb 100644
--- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/DBeaverPreferences.java
+++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/DBeaverPreferences.java
@@ -50,7 +50,7 @@ public final class DBeaverPreferences
     public static final String UI_DISCONNECT_ON_EDITORS_CLOSE = DatabaseEditorPreferences.PROP_DISCONNECT_ON_EDITORS_CLOSE; //$NON-NLS-1$
     public static final String UI_USE_EMBEDDED_AUTH = "ui.use.redirect.auth"; //$NON-NLS-1$
     public static final String UI_SHOW_HOLIDAY_DECORATIONS = HolidayDecorations.PREF_UI_SHOW_HOLIDAY_DECORATIONS;
-    public static final String UI_STATUS_BAR_SHOW_BREADCRUMBS = "ui.statusBar.show.breadcrumbs"; //$NON-NLS-1$
+    public static final String UI_STATUS_BAR_SHOW_BREADCRUMBS = DatabaseEditorPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS;
     public static final String UI_STATUS_BAR_SHOW_STATUS_LINE = "ui.statusBar.show.statusLine"; //$NON-NLS-1$
 
     // Resources
diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreMessages.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreMessages.java
index e6e2003111b9..a758779290cb 100644
--- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreMessages.java
+++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreMessages.java
@@ -370,6 +370,8 @@ public class CoreMessages extends NLS {
     public static String pref_page_ui_status_bar;
     public static String pref_page_ui_status_bar_show_breadcrumbs_check_label;
     public static String pref_page_ui_status_bar_show_breadcrumbs_check_tip;
+    public static String pref_page_ui_status_bar_show_breadcrumbs_status_bar_label;
+    public static String pref_page_ui_status_bar_show_breadcrumbs_editors_label;
     public static String pref_page_ui_status_bar_show_status_line_check_label;
     public static String pref_page_ui_status_bar_show_status_line_check_tip;
     public static String pref_page_eclipse_ui_general_group_general;
diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources.properties b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources.properties
index ea7380322758..5dc7f34be07b 100644
--- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources.properties
+++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/CoreResources.properties
@@ -490,8 +490,10 @@ pref_page_ui_general_group_notifications = Notifications
 pref_page_ui_general_group_regional = Regional settings
 pref_page_ui_general_group_resources = Resources
 pref_page_ui_status_bar = Status bar
-pref_page_ui_status_bar_show_breadcrumbs_check_label = Show breadcrumbs
+pref_page_ui_status_bar_show_breadcrumbs_check_label = Show breadcrumbs in
 pref_page_ui_status_bar_show_breadcrumbs_check_tip = Reflects current selection with the complete path to the database object
+pref_page_ui_status_bar_show_breadcrumbs_status_bar_label = Status bar
+pref_page_ui_status_bar_show_breadcrumbs_editors_label = Object editors
 pref_page_ui_status_bar_show_status_line_check_label = Show status line
 pref_page_ui_status_bar_show_status_line_check_tip = Shows description of selected objects and additional contextual information, e.g., in SQL editor
 #DatabaseGeneral
diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/DesktopPreferencesInitializer.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/DesktopPreferencesInitializer.java
index e6551fba8b89..bb689d9c7d2c 100644
--- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/DesktopPreferencesInitializer.java
+++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/core/DesktopPreferencesInitializer.java
@@ -26,6 +26,7 @@
 import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
 import org.jkiss.dbeaver.model.qm.QMConstants;
 import org.jkiss.dbeaver.model.qm.QMObjectType;
+import org.jkiss.dbeaver.ui.editors.DatabaseEditorPreferences.BreadcrumbLocation;
 import org.jkiss.dbeaver.ui.screenreaders.ScreenReader;
 import org.jkiss.dbeaver.ui.screenreaders.ScreenReaderPreferences;
 import org.jkiss.dbeaver.utils.PrefUtils;
@@ -62,7 +63,7 @@ public void initializeDefaultPreferences() {
             !ApplicationPolicyService.getInstance().isInstallUpdateDisabled());
         PrefUtils.setDefaultPreferenceValue(store, DBeaverPreferences.UI_USE_EMBEDDED_AUTH, false);
         PrefUtils.setDefaultPreferenceValue(store, DBeaverPreferences.UI_SHOW_HOLIDAY_DECORATIONS, true);
-        PrefUtils.setDefaultPreferenceValue(store, DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS, true);
+        PrefUtils.setDefaultPreferenceValue(store, DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS, BreadcrumbLocation.IN_STATUS_BAR);
         PrefUtils.setDefaultPreferenceValue(store, DBeaverPreferences.UI_STATUS_BAR_SHOW_STATUS_LINE, true);
 
         PrefUtils.setDefaultPreferenceValue(store, DBeaverPreferences.UI_KEEP_DATABASE_EDITORS, true);
diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/BreadcrumbTrim.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/BreadcrumbTrim.java
index 0850ef1cd8e5..c397870027e2 100644
--- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/BreadcrumbTrim.java
+++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/controls/BreadcrumbTrim.java
@@ -19,32 +19,30 @@
 import jakarta.annotation.PostConstruct;
 import org.eclipse.e4.ui.model.application.ui.MUIElement;
 import org.eclipse.e4.ui.workbench.modeling.EModelService;
-import org.eclipse.jface.viewers.*;
-import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.SWT;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.ui.*;
 import org.eclipse.ui.internal.Workbench;
 import org.eclipse.ui.internal.WorkbenchWindow;
 import org.jkiss.code.NotNull;
 import org.jkiss.code.Nullable;
-import org.jkiss.dbeaver.DBException;
 import org.jkiss.dbeaver.DBeaverPreferences;
 import org.jkiss.dbeaver.Log;
 import org.jkiss.dbeaver.model.DBPDataSourceContainerProvider;
 import org.jkiss.dbeaver.model.app.DBPProject;
-import org.jkiss.dbeaver.model.navigator.*;
-import org.jkiss.dbeaver.model.runtime.LocalCacheProgressMonitor;
-import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
+import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode;
+import org.jkiss.dbeaver.model.navigator.DBNModel;
+import org.jkiss.dbeaver.model.navigator.DBNNode;
 import org.jkiss.dbeaver.runtime.DBWorkbench;
 import org.jkiss.dbeaver.ui.AbstractPartListener;
-import org.jkiss.dbeaver.ui.DBeaverIcons;
 import org.jkiss.dbeaver.ui.UIExecutionQueue;
 import org.jkiss.dbeaver.ui.UIUtils;
 import org.jkiss.dbeaver.ui.actions.AbstractPageListener;
 import org.jkiss.dbeaver.ui.controls.breadcrumb.BreadcrumbViewer;
+import org.jkiss.dbeaver.ui.editors.DatabaseEditorPreferences.BreadcrumbLocation;
 import org.jkiss.dbeaver.ui.editors.ILazyEditorInput;
 import org.jkiss.dbeaver.ui.editors.INavigatorEditorInput;
-import org.jkiss.utils.ArrayUtils;
+import org.jkiss.dbeaver.ui.navigator.breadcrumb.NodeBreadcrumbViewer;
 
 import java.util.function.Consumer;
 
@@ -56,20 +54,10 @@ public class BreadcrumbTrim {
 
     @PostConstruct
     public void createControls(Composite parent) {
-        var breadcrumb = new BreadcrumbViewer(parent) {
-            @Override
-            protected void configureDropDownViewer(@NotNull TreeViewer viewer, @NotNull Object input) {
-                viewer.setContentProvider(new BreadcrumbNodeContentProvider(true));
-                viewer.setLabelProvider(new BreadcrumbNodeLabelProvider());
-            }
-        };
-        breadcrumb.setLabelProvider(new BreadcrumbNodeLabelProvider());
-        breadcrumb.setContentProvider(new BreadcrumbNodeContentProvider(false));
-        breadcrumb.addOpenListener(e -> openEditor(e.getSelection()));
-        breadcrumb.addDoubleClickListener(e -> openEditor(e.getSelection()));
+        var viewer = new NodeBreadcrumbViewer(parent, SWT.BOTTOM);
 
-        installListeners(breadcrumb);
-        updateElementVisibility();
+        installListeners(viewer);
+        UIUtils.asyncExec(BreadcrumbTrim::updateElementVisibility);
     }
 
     private static void updateElementVisibility() {
@@ -88,7 +76,7 @@ private static void updateElementVisibility(@NotNull WorkbenchWindow window) {
         boolean dirty = false;
 
         var breadcrumbsElement = modelService.find(BREADCRUMBS_ID, model);
-        var breadcrumbsVisible = store.getBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS);
+        var breadcrumbsVisible = BreadcrumbLocation.get(store) == BreadcrumbLocation.IN_STATUS_BAR;
         if (breadcrumbsElement != null && breadcrumbsElement.isToBeRendered() != breadcrumbsVisible) {
             breadcrumbsElement.setToBeRendered(breadcrumbsVisible);
             dirty = true;
@@ -109,12 +97,6 @@ private static void updateElementVisibility(@NotNull WorkbenchWindow window) {
         }
     }
 
-    private static void openEditor(@NotNull ISelection selection) {
-        if (selection instanceof IStructuredSelection ss && ss.getFirstElement() instanceof DBNNode node) {
-            DBWorkbench.getPlatformUI().openEntityEditor(node, null);
-        }
-    }
-
     private static void installListeners(@NotNull BreadcrumbViewer viewer) {
         var propertyListener = new IPropertyListener() {
             @Override
@@ -201,6 +183,9 @@ public void pageClosed(IWorkbenchPage page) {
     }
 
     private static void setInput(@NotNull BreadcrumbViewer viewer, @NotNull IEditorInput input) {
+        if (viewer.getControl().isDisposed()) {
+            return;
+        }
         if (!tryExtractNode(input, viewer::setInput)) {
             viewer.setInput(null);
         }
@@ -230,71 +215,4 @@ private static boolean tryExtractNode(@NotNull IEditorInput input, @NotNull Cons
         }
         return false;
     }
-
-    private static class BreadcrumbNodeLabelProvider extends LabelProvider {
-        @Override
-        public Image getImage(Object element) {
-            return DBeaverIcons.getImage(((DBNNode) element).getNodeIconDefault());
-        }
-
-        @Override
-        public String getText(Object element) {
-            return ((DBNNode) element).getNodeDisplayName();
-        }
-    }
-
-    private record BreadcrumbNodeContentProvider(boolean allowFoldersOnly) implements ITreeContentProvider {
-        @Override
-        public Object[] getElements(Object inputElement) {
-            DBNNode child = (DBNNode) inputElement;
-            DBNNode parent = child.getParentNode();
-            if (parent != null) {
-                return getChildren(parent);
-            }
-            return new Object[0];
-        }
-
-        @Override
-        public Object getParent(Object element) {
-            DBNNode child = (DBNNode) element;
-            if (child instanceof DBNDataSource) {
-                return null; // don't show anything below data sources
-            }
-
-            DBNNode parent = child.getParentNode();
-            while (parent instanceof DBNDatabaseFolder) {
-                parent = parent.getParentNode(); // skip folder nodes
-            }
-
-            return parent;
-        }
-
-        @Override
-        public Object[] getChildren(Object parentElement) {
-            var children = getCachedChildren((DBNNode) parentElement);
-            if (children != null) {
-                return children;
-            }
-            return new Object[0];
-        }
-
-        @Override
-        public boolean hasChildren(Object element) {
-            if (!allowFoldersOnly || element instanceof DBNLocalFolder) {
-                return !ArrayUtils.isEmpty(getCachedChildren((DBNNode) element));
-            } else {
-                return false;
-            }
-        }
-
-        @Nullable
-        private static DBNNode[] getCachedChildren(@NotNull DBNNode parent) {
-            try {
-                return parent.getChildren(new LocalCacheProgressMonitor(new VoidProgressMonitor()));
-            } catch (DBException e) {
-                log.error("Error getting children", e); // should not happen
-                return null;
-            }
-        }
-    }
 }
diff --git a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageDatabaseUserInterface.java b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageDatabaseUserInterface.java
index 614d08f9ad40..1af14d2cfb84 100644
--- a/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageDatabaseUserInterface.java
+++ b/plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageDatabaseUserInterface.java
@@ -22,10 +22,7 @@
 import org.eclipse.jface.fieldassist.IContentProposal;
 import org.eclipse.jface.fieldassist.IContentProposalProvider;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.ModifyEvent;
-import org.eclipse.swt.events.ModifyListener;
-import org.eclipse.swt.events.SelectionAdapter;
-import org.eclipse.swt.events.SelectionEvent;
+import org.eclipse.swt.events.*;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.*;
 import org.eclipse.ui.IWorkbench;
@@ -51,6 +48,8 @@
 import org.jkiss.dbeaver.runtime.DBWorkbench;
 import org.jkiss.dbeaver.ui.UIUtils;
 import org.jkiss.dbeaver.ui.contentassist.ContentAssistUtils;
+import org.jkiss.dbeaver.ui.editors.DatabaseEditorPreferences;
+import org.jkiss.dbeaver.ui.editors.DatabaseEditorPreferences.BreadcrumbLocation;
 import org.jkiss.dbeaver.utils.GeneralUtils;
 import org.jkiss.dbeaver.utils.PrefUtils;
 import org.jkiss.dbeaver.utils.RuntimeUtils;
@@ -79,7 +78,7 @@ public class PrefPageDatabaseUserInterface extends AbstractPrefPage implements I
 
     private Button statusBarShowBreadcrumbsCheck;
     private Button statusBarShowStatusCheck;
-
+    private Combo statusBarBreadcrumbPositionCombo;
 
     public PrefPageDatabaseUserInterface()
     {
@@ -226,7 +225,7 @@ public void widgetSelected(SelectionEvent e) {
         Group breadcrumbs = UIUtils.createControlGroup(
             composite,
             CoreMessages.pref_page_ui_status_bar,
-            1,
+            2,
             GridData.FILL_HORIZONTAL | GridData.VERTICAL_ALIGN_BEGINNING,
             0
         );
@@ -237,12 +236,21 @@ public void widgetSelected(SelectionEvent e) {
             true,
             1
         );
+        statusBarShowBreadcrumbsCheck.addSelectionListener(SelectionListener.widgetSelectedAdapter(e -> {
+            statusBarBreadcrumbPositionCombo.setEnabled(statusBarShowBreadcrumbsCheck.getSelection());
+        }));
+
+        statusBarBreadcrumbPositionCombo = new Combo(breadcrumbs, SWT.READ_ONLY | SWT.DROP_DOWN);
+        statusBarBreadcrumbPositionCombo.add(CoreMessages.pref_page_ui_status_bar_show_breadcrumbs_status_bar_label);
+        statusBarBreadcrumbPositionCombo.add(CoreMessages.pref_page_ui_status_bar_show_breadcrumbs_editors_label);
+        statusBarBreadcrumbPositionCombo.select(0);
+
         statusBarShowStatusCheck = UIUtils.createCheckbox(
             breadcrumbs,
             CoreMessages.pref_page_ui_status_bar_show_status_line_check_label,
             CoreMessages.pref_page_ui_status_bar_show_status_line_check_tip,
             true,
-            1
+            2
         );
 
         setSettings();
@@ -271,7 +279,10 @@ private void setSettings() {
             }
         }
 
-        statusBarShowBreadcrumbsCheck.setSelection(store.getBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS));
+        BreadcrumbLocation breadcrumbLocation = DatabaseEditorPreferences.BreadcrumbLocation.get(store);
+        statusBarShowBreadcrumbsCheck.setSelection(breadcrumbLocation != DatabaseEditorPreferences.BreadcrumbLocation.HIDDEN);
+        statusBarBreadcrumbPositionCombo.select(breadcrumbLocation == DatabaseEditorPreferences.BreadcrumbLocation.IN_EDITORS ? 1 : 0);
+        statusBarBreadcrumbPositionCombo.setEnabled(statusBarShowBreadcrumbsCheck.getSelection());
         statusBarShowStatusCheck.setSelection(store.getBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_STATUS_LINE));
     }
 
@@ -292,7 +303,9 @@ protected void performDefaults() {
             UIUtils.setComboSelection(clientTimezone, store.getDefaultString(ModelPreferences.CLIENT_TIMEZONE));
         }
 
-        statusBarShowBreadcrumbsCheck.setSelection(store.getDefaultBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS));
+        BreadcrumbLocation location = BreadcrumbLocation.getDefault(store);
+        statusBarShowBreadcrumbsCheck.setSelection(location != BreadcrumbLocation.HIDDEN);
+        statusBarBreadcrumbPositionCombo.select(location == BreadcrumbLocation.IN_STATUS_BAR ? 0 : 1);
         statusBarShowStatusCheck.setSelection(store.getDefaultBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_STATUS_LINE));
     }
 
@@ -355,7 +368,16 @@ public boolean performOk()
             }
         }
 
-        store.setValue(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS, statusBarShowBreadcrumbsCheck.getSelection());
+        BreadcrumbLocation breadcrumbLocation;
+        if (!statusBarShowBreadcrumbsCheck.getSelection()) {
+            breadcrumbLocation = DatabaseEditorPreferences.BreadcrumbLocation.HIDDEN;
+        } else if (statusBarBreadcrumbPositionCombo.getSelectionIndex() == 0) {
+            breadcrumbLocation = DatabaseEditorPreferences.BreadcrumbLocation.IN_STATUS_BAR;
+        } else {
+            breadcrumbLocation = DatabaseEditorPreferences.BreadcrumbLocation.IN_EDITORS;
+        }
+
+        store.setValue(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS, breadcrumbLocation.name());
         store.setValue(DBeaverPreferences.UI_STATUS_BAR_SHOW_STATUS_LINE, statusBarShowStatusCheck.getSelection());
 
         return true;
diff --git a/plugins/org.jkiss.dbeaver.ui.editors.base/src/org/jkiss/dbeaver/ui/editors/DatabaseEditorPreferences.java b/plugins/org.jkiss.dbeaver.ui.editors.base/src/org/jkiss/dbeaver/ui/editors/DatabaseEditorPreferences.java
index 7617dfa7a179..ce20aaae849b 100644
--- a/plugins/org.jkiss.dbeaver.ui.editors.base/src/org/jkiss/dbeaver/ui/editors/DatabaseEditorPreferences.java
+++ b/plugins/org.jkiss.dbeaver.ui.editors.base/src/org/jkiss/dbeaver/ui/editors/DatabaseEditorPreferences.java
@@ -1,6 +1,6 @@
 /*
  * DBeaver - Universal Database Manager
- * Copyright (C) 2010-2024 DBeaver Corp and others
+ * Copyright (C) 2010-2025 DBeaver Corp and others
  *
  * Licensed under the Apache License, Version 2.0 (the "License");
  * you may not use this file except in compliance with the License.
@@ -16,12 +16,43 @@
  */
 package org.jkiss.dbeaver.ui.editors;
 
+import org.jkiss.code.NotNull;
+import org.jkiss.code.Nullable;
+import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
+import org.jkiss.utils.CommonUtils;
+
 /**
  * DatabaseEditorPreferences
  */
 public class DatabaseEditorPreferences {
+    public enum BreadcrumbLocation {
+        IN_STATUS_BAR,
+        IN_EDITORS,
+        HIDDEN;
+
+        @NotNull
+        public static BreadcrumbLocation get(@NotNull DBPPreferenceStore store) {
+            return get(store.getString(UI_STATUS_BAR_SHOW_BREADCRUMBS));
+        }
+
+        @NotNull
+        public static BreadcrumbLocation getDefault(@NotNull DBPPreferenceStore store) {
+            return get(store.getDefaultString(UI_STATUS_BAR_SHOW_BREADCRUMBS));
+        }
+
+        @NotNull
+        private static BreadcrumbLocation get(@Nullable String value) {
+            return switch (CommonUtils.notEmpty(value)) {
+                case "HIDDEN", "false" -> HIDDEN;
+                case "IN_EDITORS" -> IN_EDITORS;
+                default -> IN_STATUS_BAR;
+            };
+        }
+    }
+
     public static final String PROP_TITLE_SHOW_FULL_NAME = "navigator.editor.full-name"; //$NON-NLS-1$
     public static final String PROP_SAVE_EDITORS_STATE = "ui.editors.reopen-after-restart"; //$NON-NLS-1$
     public static final String PROP_KEEP_EDITORS_ON_DISCONNECT = "ui.editors.keep-editors-on-disconnect"; //$NON-NLS-1$
     public static final String PROP_DISCONNECT_ON_EDITORS_CLOSE = "ui.editors.disconnect-on-editors-close"; //$NON-NLS-1$
+    public static final String UI_STATUS_BAR_SHOW_BREADCRUMBS = "ui.statusBar.show.breadcrumbs"; //$NON-NLS-1$
 }
diff --git a/plugins/org.jkiss.dbeaver.ui.editors.base/src/org/jkiss/dbeaver/ui/editors/MultiPageAbstractEditor.java b/plugins/org.jkiss.dbeaver.ui.editors.base/src/org/jkiss/dbeaver/ui/editors/MultiPageAbstractEditor.java
index 8335e3f42bfd..988655e568dc 100644
--- a/plugins/org.jkiss.dbeaver.ui.editors.base/src/org/jkiss/dbeaver/ui/editors/MultiPageAbstractEditor.java
+++ b/plugins/org.jkiss.dbeaver.ui.editors.base/src/org/jkiss/dbeaver/ui/editors/MultiPageAbstractEditor.java
@@ -28,6 +28,8 @@
 import org.eclipse.swt.widgets.*;
 import org.eclipse.ui.*;
 import org.eclipse.ui.part.MultiPageEditorPart;
+import org.jkiss.code.NotNull;
+import org.jkiss.dbeaver.Log;
 import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
 import org.jkiss.dbeaver.runtime.DBWorkbench;
 import org.jkiss.dbeaver.ui.*;
@@ -35,6 +37,7 @@
 import org.jkiss.dbeaver.ui.screenreaders.ScreenReaderPreferences;
 import org.jkiss.utils.CommonUtils;
 
+import java.lang.reflect.Method;
 import java.util.ArrayList;
 import java.util.List;
 
@@ -42,6 +45,8 @@
  * MultiPageAbstractEditor
  */
 public abstract class MultiPageAbstractEditor extends MultiPageEditorPart {
+    private static final Log log = Log.getLog(MultiPageAbstractEditor.class);
+
     private ImageDescriptor curTitleImage;
     private final List<Image> oldImages = new ArrayList<>();
     private int activePageIndex = -1;
@@ -141,25 +146,33 @@ protected void setContainerStyles() {
             tabFolder.setMRUVisible(true);
             tabFolder.setTabPosition(SWT.TOP);
             Control topRight = createTopRightControl(tabFolder);
+
+            int tabHeight = getDefaultTabHeight(tabFolder);
             if (topRight != null) {
-                Point trSize = topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT);
-                tabFolder.setTabHeight(trSize.y);
                 tabFolder.setTopRight(topRight, SWT.RIGHT | SWT.WRAP);
-            } else {
-                // Sample toolbar's height as it fits quite nicely.
-                ToolBar toolBar = new ToolBar(tabFolder, SWT.FLAT | SWT.RIGHT);
-                // Add a dummy item as empty toolbars are considered {0, 0} on some platforms
-                new ToolItem(toolBar, SWT.PUSH).setImage(DBeaverIcons.getImage(UIIcon.SEPARATOR_V));
-                tabFolder.setTabHeight(toolBar.computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
-                toolBar.dispose();
+                tabHeight = Math.max(tabHeight, topRight.computeSize(SWT.DEFAULT, SWT.DEFAULT).y);
             }
+            tabFolder.setTabHeight(tabHeight);
         }
     }
 
+    private static int getDefaultTabHeight(@NotNull CTabFolder tabFolder) {
+        // Sample toolbar's height as it fits quite nicely.
+        ToolBar toolBar = new ToolBar(tabFolder, SWT.FLAT | SWT.RIGHT);
+
+        // Add a dummy item as empty toolbars are considered {0, 0} on some platforms
+        ToolItem item = new ToolItem(toolBar, SWT.PUSH);
+        item.setImage(DBeaverIcons.getImage(UIIcon.SEPARATOR_V));
+
+        Point size = toolBar.computeSize(SWT.DEFAULT, SWT.DEFAULT);
+        toolBar.dispose();
+
+        return size.y;
+    }
+
     protected void setPageToolTip(int index, String toolTip) {
         Composite pageContainer = getContainer();
-        if (pageContainer instanceof CTabFolder) {
-            CTabFolder tabFolder = (CTabFolder) pageContainer;
+        if (pageContainer instanceof CTabFolder tabFolder) {
             if (index < tabFolder.getItemCount()) {
                 tabFolder.getItem(index).setToolTipText(toolTip);
             }
@@ -220,6 +233,22 @@ protected Control createTopRightControl(Composite composite) {
         return null;
     }
 
+    /**
+     * Causes the top right control to be updated.
+     */
+    protected void updateTopRightControl() {
+        if (!(getContainer() instanceof CTabFolder folder) || folder.isDisposed()) {
+            return;
+        }
+        try {
+            Method updateFolder = CTabFolder.class.getDeclaredMethod("updateFolder", int.class);
+            updateFolder.setAccessible(true);
+            updateFolder.invoke(folder, 10 /*UPDATE_TAB_HEIGHT | REDRAW*/);
+        } catch (ReflectiveOperationException e) {
+            log.error("Error updating CTabFolder top right control", e);
+        }
+    }
+
     public void recreatePages() {
         int pageCount = getPageCount();
         for (int i = pageCount; i > 0; i--) {
diff --git a/plugins/org.jkiss.dbeaver.ui.navigator/META-INF/MANIFEST.MF b/plugins/org.jkiss.dbeaver.ui.navigator/META-INF/MANIFEST.MF
index c0207a4a20b9..299e3226d2da 100644
--- a/plugins/org.jkiss.dbeaver.ui.navigator/META-INF/MANIFEST.MF
+++ b/plugins/org.jkiss.dbeaver.ui.navigator/META-INF/MANIFEST.MF
@@ -26,6 +26,7 @@ Export-Package: org.jkiss.dbeaver.tools.project,
  org.jkiss.dbeaver.ui.internal,
  org.jkiss.dbeaver.ui.navigator,
  org.jkiss.dbeaver.ui.navigator.actions,
+ org.jkiss.dbeaver.ui.navigator.breadcrumb,
  org.jkiss.dbeaver.ui.navigator.database,
  org.jkiss.dbeaver.ui.navigator.database.load,
  org.jkiss.dbeaver.ui.navigator.dialogs,
diff --git a/plugins/org.jkiss.dbeaver.ui.navigator/src/org/jkiss/dbeaver/ui/editors/entity/EntityEditor.java b/plugins/org.jkiss.dbeaver.ui.navigator/src/org/jkiss/dbeaver/ui/editors/entity/EntityEditor.java
index 3fe2f018ea02..d6e2e498a4bc 100644
--- a/plugins/org.jkiss.dbeaver.ui.navigator/src/org/jkiss/dbeaver/ui/editors/entity/EntityEditor.java
+++ b/plugins/org.jkiss.dbeaver.ui.navigator/src/org/jkiss/dbeaver/ui/editors/entity/EntityEditor.java
@@ -21,8 +21,12 @@
 import org.eclipse.core.runtime.IStatus;
 import org.eclipse.core.runtime.Status;
 import org.eclipse.jface.dialogs.IDialogConstants;
+import org.eclipse.jface.layout.GridDataFactory;
+import org.eclipse.jface.layout.GridLayoutFactory;
 import org.eclipse.jface.text.IUndoManager;
+import org.eclipse.swt.SWT;
 import org.eclipse.swt.custom.CTabFolder;
+import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Display;
 import org.eclipse.ui.*;
@@ -45,6 +49,8 @@
 import org.jkiss.dbeaver.model.navigator.DBNDatabaseNode;
 import org.jkiss.dbeaver.model.navigator.DBNEvent;
 import org.jkiss.dbeaver.model.navigator.DBNNode;
+import org.jkiss.dbeaver.model.preferences.DBPPreferenceListener;
+import org.jkiss.dbeaver.model.preferences.DBPPreferenceStore;
 import org.jkiss.dbeaver.model.rm.RMConstants;
 import org.jkiss.dbeaver.model.runtime.AbstractJob;
 import org.jkiss.dbeaver.model.runtime.DBRProgressMonitor;
@@ -61,14 +67,17 @@
 import org.jkiss.dbeaver.ui.controls.CustomFormEditor;
 import org.jkiss.dbeaver.ui.controls.ProgressPageControl;
 import org.jkiss.dbeaver.ui.controls.PropertyPageStandard;
+import org.jkiss.dbeaver.ui.controls.breadcrumb.BreadcrumbViewer;
 import org.jkiss.dbeaver.ui.controls.folders.ITabbedFolder;
 import org.jkiss.dbeaver.ui.controls.folders.ITabbedFolderContainer;
 import org.jkiss.dbeaver.ui.controls.folders.ITabbedFolderListener;
 import org.jkiss.dbeaver.ui.dialogs.ConfirmationDialog;
 import org.jkiss.dbeaver.ui.editors.*;
+import org.jkiss.dbeaver.ui.editors.DatabaseEditorPreferences.BreadcrumbLocation;
 import org.jkiss.dbeaver.ui.editors.entity.properties.ObjectPropertiesEditor;
 import org.jkiss.dbeaver.ui.internal.UINavigatorMessages;
 import org.jkiss.dbeaver.ui.navigator.NavigatorPreferences;
+import org.jkiss.dbeaver.ui.navigator.breadcrumb.NodeBreadcrumbViewer;
 import org.jkiss.dbeaver.utils.GeneralUtils;
 import org.jkiss.utils.CommonUtils;
 
@@ -1068,6 +1077,30 @@ public <T> T getNestedAdapter(Class<T> adapter) {
         return null;
     }
 
+    @Override
+    protected Control createTopRightControl(Composite parent) {
+        Composite composite = new Composite(parent, SWT.NONE);
+        composite.setLayoutData(GridDataFactory.swtDefaults().grab(false, true).create());
+        composite.setLayout(GridLayoutFactory.fillDefaults().create());
+
+        NodeBreadcrumbViewer viewer = new NodeBreadcrumbViewer(composite, SWT.TOP);
+        viewer.setInput(getEditorInput().getNavigatorNode());
+
+        DBPPreferenceStore store = DBWorkbench.getPlatform().getPreferenceStore();
+        DBPPreferenceListener listener = event -> {
+            if (event.getProperty().equals(DatabaseEditorPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS)) {
+                composite.setVisible(BreadcrumbLocation.get(store) == BreadcrumbLocation.IN_EDITORS);
+                updateTopRightControl();
+            }
+        };
+
+        store.addPropertyChangeListener(listener);
+        composite.addDisposeListener(e -> store.removePropertyChangeListener(listener));
+        composite.setVisible(BreadcrumbLocation.get(store) == BreadcrumbLocation.IN_EDITORS);
+
+        return composite;
+    }
+
     @Override
     public DBPDataSourceContainer getDataSourceContainer() {
         return DBUtils.getContainer(getDatabaseObject());
diff --git a/plugins/org.jkiss.dbeaver.ui.navigator/src/org/jkiss/dbeaver/ui/navigator/breadcrumb/NodeBreadcrumbViewer.java b/plugins/org.jkiss.dbeaver.ui.navigator/src/org/jkiss/dbeaver/ui/navigator/breadcrumb/NodeBreadcrumbViewer.java
new file mode 100644
index 000000000000..8120d5171b78
--- /dev/null
+++ b/plugins/org.jkiss.dbeaver.ui.navigator/src/org/jkiss/dbeaver/ui/navigator/breadcrumb/NodeBreadcrumbViewer.java
@@ -0,0 +1,129 @@
+/*
+ * DBeaver - Universal Database Manager
+ * Copyright (C) 2010-2025 DBeaver Corp and others
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.jkiss.dbeaver.ui.navigator.breadcrumb;
+
+import org.eclipse.jface.viewers.ISelection;
+import org.eclipse.jface.viewers.IStructuredSelection;
+import org.eclipse.jface.viewers.ITreeContentProvider;
+import org.eclipse.jface.viewers.LabelProvider;
+import org.eclipse.swt.graphics.Image;
+import org.eclipse.swt.widgets.Composite;
+import org.jkiss.code.NotNull;
+import org.jkiss.code.Nullable;
+import org.jkiss.dbeaver.DBException;
+import org.jkiss.dbeaver.Log;
+import org.jkiss.dbeaver.model.navigator.DBNDataSource;
+import org.jkiss.dbeaver.model.navigator.DBNDatabaseFolder;
+import org.jkiss.dbeaver.model.navigator.DBNLocalFolder;
+import org.jkiss.dbeaver.model.navigator.DBNNode;
+import org.jkiss.dbeaver.model.runtime.LocalCacheProgressMonitor;
+import org.jkiss.dbeaver.model.runtime.VoidProgressMonitor;
+import org.jkiss.dbeaver.runtime.DBWorkbench;
+import org.jkiss.dbeaver.ui.DBeaverIcons;
+import org.jkiss.dbeaver.ui.controls.breadcrumb.BreadcrumbViewer;
+import org.jkiss.utils.ArrayUtils;
+
+/**
+ * A {@link DBNNode}-oriented specialization of {@link BreadcrumbViewer}.
+ */
+public class NodeBreadcrumbViewer extends BreadcrumbViewer {
+    private static final Log log = Log.getLog(NodeBreadcrumbViewer.class);
+
+    public NodeBreadcrumbViewer(@NotNull Composite parent, int style) {
+        super(parent, style);
+
+        setLabelProvider(new BreadcrumbNodeLabelProvider());
+        setContentProvider(new BreadcrumbNodeContentProvider(false));
+        setDropDownContentProvider(new BreadcrumbNodeContentProvider(true));
+
+        addOpenListener(e -> openEditor(e.getSelection()));
+        addDoubleClickListener(e -> openEditor(e.getSelection()));
+    }
+
+    private static void openEditor(@NotNull ISelection selection) {
+        if (selection instanceof IStructuredSelection ss && ss.getFirstElement() instanceof DBNNode node) {
+            DBWorkbench.getPlatformUI().openEntityEditor(node, null);
+        }
+    }
+
+    private static class BreadcrumbNodeLabelProvider extends LabelProvider {
+        @Override
+        public Image getImage(Object element) {
+            return DBeaverIcons.getImage(((DBNNode) element).getNodeIconDefault());
+        }
+
+        @Override
+        public String getText(Object element) {
+            return ((DBNNode) element).getNodeDisplayName();
+        }
+    }
+
+    private record BreadcrumbNodeContentProvider(boolean allowFoldersOnly) implements ITreeContentProvider {
+        @Override
+        public Object[] getElements(Object inputElement) {
+            DBNNode child = (DBNNode) inputElement;
+            DBNNode parent = child.getParentNode();
+            if (parent != null) {
+                return getChildren(parent);
+            }
+            return new Object[0];
+        }
+
+        @Override
+        public Object getParent(Object element) {
+            DBNNode child = (DBNNode) element;
+            if (child instanceof DBNDataSource) {
+                return null; // don't show anything below data sources
+            }
+
+            DBNNode parent = child.getParentNode();
+            while (parent instanceof DBNDatabaseFolder) {
+                parent = parent.getParentNode(); // skip folder nodes
+            }
+
+            return parent;
+        }
+
+        @Override
+        public Object[] getChildren(Object parentElement) {
+            var children = getCachedChildren((DBNNode) parentElement);
+            if (children != null) {
+                return children;
+            }
+            return new Object[0];
+        }
+
+        @Override
+        public boolean hasChildren(Object element) {
+            if (!allowFoldersOnly || element instanceof DBNLocalFolder) {
+                return !ArrayUtils.isEmpty(getCachedChildren((DBNNode) element));
+            } else {
+                return false;
+            }
+        }
+
+        @Nullable
+        private static DBNNode[] getCachedChildren(@NotNull DBNNode parent) {
+            try {
+                return parent.getChildren(new LocalCacheProgressMonitor(new VoidProgressMonitor()));
+            } catch (DBException e) {
+                log.error("Error getting children", e); // should not happen
+                return null;
+            }
+        }
+    }
+}
diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/breadcrumb/BreadcrumbItem.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/breadcrumb/BreadcrumbItem.java
index c6845a8244bd..b5d7e68c3c28 100644
--- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/breadcrumb/BreadcrumbItem.java
+++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/breadcrumb/BreadcrumbItem.java
@@ -16,32 +16,31 @@
  */
 package org.jkiss.dbeaver.ui.controls.breadcrumb;
 
+import org.eclipse.jface.action.Action;
+import org.eclipse.jface.action.MenuManager;
 import org.eclipse.jface.layout.GridLayoutFactory;
-import org.eclipse.jface.util.OpenStrategy;
+import org.eclipse.jface.resource.ImageDescriptor;
 import org.eclipse.jface.viewers.ILabelProvider;
-import org.eclipse.jface.viewers.IStructuredSelection;
 import org.eclipse.jface.viewers.ITreeContentProvider;
-import org.eclipse.jface.viewers.TreeViewer;
 import org.eclipse.swt.SWT;
-import org.eclipse.swt.events.*;
+import org.eclipse.swt.events.MouseEvent;
 import org.eclipse.swt.graphics.Image;
 import org.eclipse.swt.graphics.Point;
 import org.eclipse.swt.graphics.Rectangle;
-import org.eclipse.swt.layout.FillLayout;
 import org.eclipse.swt.layout.GridData;
 import org.eclipse.swt.widgets.*;
 import org.jkiss.code.NotNull;
 import org.jkiss.code.Nullable;
 import org.jkiss.dbeaver.ui.DBeaverIcons;
+import org.jkiss.dbeaver.ui.EmptyAction;
 import org.jkiss.dbeaver.ui.UIIcon;
 import org.jkiss.dbeaver.ui.UIUtils;
 import org.jkiss.dbeaver.ui.controls.DoubleClickMouseAdapter;
+import org.jkiss.dbeaver.utils.NLS;
 import org.jkiss.utils.CommonUtils;
 
 final class BreadcrumbItem extends Item {
-    private static final int DROP_DOWN_MIN_WIDTH = 250;
-    private static final int DROP_DOWN_MAX_WIDTH = 500;
-    private static final int DROP_DOWN_MIN_HEIGHT = 10;
+    private static final int DROP_DOWN_MAX_ITEMS = 30;
 
     private final BreadcrumbViewer viewer;
 
@@ -52,13 +51,12 @@ final class BreadcrumbItem extends Item {
     private final Composite detailComposite;
     private final Composite imageComposite;
     private final Composite textComposite;
+    private final MenuManager menuManager;
 
     private ILabelProvider labelProvider;
     private ITreeContentProvider contentProvider;
     private ILabelProvider toolTipLabelProvider;
 
-    private Shell menuShell;
-    private TreeViewer menuViewer;
     private boolean showText = true;
 
     public BreadcrumbItem(@NotNull BreadcrumbViewer viewer, @NotNull Composite parent) {
@@ -96,12 +94,38 @@ public BreadcrumbItem(@NotNull BreadcrumbViewer viewer, @NotNull Composite paren
         addElementListener(textComposite);
         addElementListener(elementImage);
         addElementListener(elementText);
+
+        menuManager = new MenuManager();
+        menuManager.setRemoveAllWhenShown(true);
+        menuManager.addMenuListener(manager -> {
+            var contentProvider = viewer.getDropDownContentProvider();
+            var elements = contentProvider.getElements(getData());
+
+            for (int i = 0; i < Math.min(elements.length, DROP_DOWN_MAX_ITEMS); i++) {
+                var element = elements[i];
+                var labelProvider = (ILabelProvider) viewer.getLabelProvider();
+                var name = labelProvider.getText(element);
+                var image = labelProvider.getImage(element);
+
+                manager.add(new Action(name, ImageDescriptor.createFromImage(image)) {
+                    @Override
+                    public void run() {
+                        openElement(element);
+                    }
+                });
+            }
+
+            if (elements.length > DROP_DOWN_MAX_ITEMS) {
+                manager.add(new EmptyAction(NLS.bind("... {0} more", elements.length - DROP_DOWN_MAX_ITEMS)));
+            }
+        });
     }
 
     @Override
     public void dispose() {
         super.dispose();
         container.dispose();
+        menuManager.dispose();
     }
 
     public void refresh() {
@@ -191,50 +215,20 @@ private void setArrowVisible(boolean visible) {
     }
 
     private void showMenu() {
-        menuShell = new Shell(container.getShell(), SWT.RESIZE | SWT.TOOL | SWT.ON_TOP);
-        menuShell.setLayout(new FillLayout());
+        Point location = detailComposite.toDisplay(0, 0);
 
-        Object input = getData();
-
-        menuViewer = new TreeViewer(menuShell, SWT.SINGLE | SWT.H_SCROLL | SWT.V_SCROLL);
-        menuViewer.addOpenListener(e -> {
-            if (e.getSelection() instanceof IStructuredSelection selection) {
-                Object element = selection.getFirstElement();
-                if (element != null) {
-                    openElement(element);
-                }
-            }
-        });
-        menuViewer.getTree().addMouseListener(new MouseAdapter() {
-            @Override
-            public void mouseUp(MouseEvent e) {
-                if (e.button != 1 || (OpenStrategy.getOpenMethod() & OpenStrategy.SINGLE_CLICK) != 0) {
-                    return;
-                }
-                Tree tree = (Tree) e.widget;
-                Item item = tree.getItem(new Point(e.x, e.y));
-                if (item != null) {
-                    openElement(item.getData());
-                }
-            }
-        });
-
-        viewer.configureDropDownViewer(menuViewer, input);
-        menuViewer.setInput(input);
-
-        configureShellBounds(menuShell);
-        configureShellCloser(menuShell);
+        if (CommonUtils.isBitSet(viewer.getStyle(), SWT.TOP)) {
+            // Adjust the location so the menu won't cover the viewer
+            location.y += detailComposite.getSize().y;
+        }
 
-        menuShell.setVisible(true);
-        menuShell.setFocus();
+        Menu menu = menuManager.createContextMenu(container);
+        menu.setLocation(location);
+        menu.setVisible(true);
     }
 
     private void openElement(@NotNull Object element) {
         viewer.fireMenuSelection(element);
-
-        if (menuShell != null) {
-            menuShell.dispose();
-        }
     }
 
     private void addElementListener(@NotNull Control control) {
@@ -253,80 +247,4 @@ public void onMouseDoubleClick(@NotNull MouseEvent e) {
             }
         });
     }
-
-    private void configureShellBounds(@NotNull Shell shell) {
-        shell.pack();
-
-        var window = UIUtils.getActiveWorkbenchWindow();
-        var windowSize = window.getShell().getSize();
-
-        var shellSize = shell.getSize();
-        int width = CommonUtils.clamp(shellSize.x, DROP_DOWN_MIN_WIDTH, DROP_DOWN_MAX_WIDTH);
-        int height = CommonUtils.clamp(shellSize.y, DROP_DOWN_MIN_HEIGHT, windowSize.y / 2);
-
-        var itemBounds = container.getBounds();
-        var trimBounds = shell.computeTrim(0, 0, width, height);
-
-        var location = new Point(0, 0);
-        location.x = trimBounds.x + elementArrow.getSize().x;
-        location.y -= height + trimBounds.y + itemBounds.y;
-
-        shell.setLocation(container.toDisplay(location));
-        shell.setSize(width, height);
-    }
-
-    private void configureShellCloser(@NotNull Shell shell) {
-        Listener focusListener = e -> {
-            switch (e.type) {
-                case SWT.FocusIn -> {
-                    Widget focusElement = e.widget;
-                    boolean isFocusBreadcrumbTreeFocusWidget = focusElement == shell || focusElement instanceof Tree tree && tree.getShell() == shell;
-                    boolean isFocusWidgetParentShell = focusElement instanceof Control control && control.getShell().getParent() == shell;
-
-                    if (!isFocusBreadcrumbTreeFocusWidget && !isFocusWidgetParentShell) {
-                        shell.close();
-                    }
-                }
-                case SWT.FocusOut -> {
-                    if (e.display.getActiveShell() == null) {
-                        shell.close();
-                    }
-                }
-            }
-        };
-
-        Display display = shell.getDisplay();
-        display.addFilter(SWT.FocusIn, focusListener);
-        display.addFilter(SWT.FocusOut, focusListener);
-
-        ControlListener controlListener = new ControlListener() {
-            @Override
-            public void controlMoved(ControlEvent e) {
-                shell.close();
-            }
-
-            @Override
-            public void controlResized(ControlEvent e) {
-                shell.close();
-            }
-        };
-
-        container.addControlListener(controlListener);
-
-        shell.addDisposeListener(e -> {
-            display.removeFilter(SWT.FocusIn, focusListener);
-            display.removeFilter(SWT.FocusOut, focusListener);
-
-            if (!container.isDisposed()) {
-                container.removeControlListener(controlListener);
-            }
-        });
-        shell.addShellListener(new ShellAdapter() {
-            @Override
-            public void shellClosed(ShellEvent e) {
-                menuShell = null;
-                menuViewer = null;
-            }
-        });
-    }
 }
diff --git a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/breadcrumb/BreadcrumbViewer.java b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/breadcrumb/BreadcrumbViewer.java
index 8596e0a8029b..1b32883245d6 100644
--- a/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/breadcrumb/BreadcrumbViewer.java
+++ b/plugins/org.jkiss.dbeaver.ui/src/org/jkiss/dbeaver/ui/controls/breadcrumb/BreadcrumbViewer.java
@@ -22,6 +22,7 @@
 import org.eclipse.jface.viewers.*;
 import org.eclipse.swt.SWT;
 import org.eclipse.swt.events.DisposeEvent;
+import org.eclipse.swt.layout.GridLayout;
 import org.eclipse.swt.widgets.Composite;
 import org.eclipse.swt.widgets.Control;
 import org.eclipse.swt.widgets.Widget;
@@ -33,19 +34,37 @@
 import java.util.List;
 import java.util.Objects;
 
-public abstract class BreadcrumbViewer extends StructuredViewer {
-    private final List<BreadcrumbItem> breadcrumbItems = new ArrayList<>();
+public class BreadcrumbViewer extends StructuredViewer {
+    // Makes breadcrumb items collapse by hiding their text if there is not enough space
+    private static final boolean COLLAPSE_ELEMENTS = false;
 
+    private final List<BreadcrumbItem> breadcrumbItems = new ArrayList<>();
     private final Composite container;
+    private final int style;
 
     private ILabelProvider toolTipLabelProvider;
+    private ITreeContentProvider dropDownContentProvider;
     private BreadcrumbItem selectedItem;
 
-    public BreadcrumbViewer(@NotNull Composite parent) {
+    /**
+     * Creates a breadcrumb viewer.
+     *
+     * @param parent the parent composite
+     * @param style  the style bits
+     * @see SWT#TOP
+     * @see SWT#BOTTOM
+     */
+    public BreadcrumbViewer(@NotNull Composite parent, int style) {
         container = new Composite(parent, SWT.NONE);
         container.setLayoutData(GridDataFactory.fillDefaults().grab(true, true).create());
-        container.setLayout(GridLayoutFactory.fillDefaults().spacing(0, 0).numColumns(1000).create());
+        container.setLayout(GridLayoutFactory.fillDefaults().spacing(0, 0).create());
         container.addListener(SWT.Resize, e -> refresh());
+
+        if (style != SWT.TOP && style != SWT.BOTTOM) {
+            throw new IllegalArgumentException("Invalid style bits");
+        }
+
+        this.style = style;
     }
 
     @Override
@@ -68,12 +87,13 @@ protected void inputChanged(Object input, Object oldInput) {
         }
 
         try (var ignored = UIUtils.disableRedraw(container)) {
-            if (!breadcrumbItems.isEmpty()) {
+            if (COLLAPSE_ELEMENTS && !breadcrumbItems.isEmpty()) {
                 breadcrumbItems.get(breadcrumbItems.size() - 1).setTrailing(false);
             }
 
             int lastIndex = buildItemChain(input);
-            if (lastIndex > 0) {
+
+            if (COLLAPSE_ELEMENTS && lastIndex > 0) {
                 breadcrumbItems.get(lastIndex - 1).setTrailing(true);
             }
 
@@ -81,6 +101,7 @@ protected void inputChanged(Object input, Object oldInput) {
                 BreadcrumbItem item = breadcrumbItems.remove(breadcrumbItems.size() - 1);
                 unmapElement(item.getData());
                 item.dispose();
+                ((GridLayout) container.getLayout()).numColumns--;
             }
 
             updateSize();
@@ -170,6 +191,12 @@ protected void setSelectionToWidget(List l, boolean reveal) {
         }
     }
 
+    @Override
+    public void setContentProvider(IContentProvider provider) {
+        super.setContentProvider(provider);
+        setDropDownContentProvider((ITreeContentProvider) provider);
+    }
+
     @Override
     protected void assertContentProviderType(IContentProvider provider) {
         Assert.isTrue(provider instanceof ITreeContentProvider);
@@ -206,23 +233,32 @@ public void setToolTipLabelProvider(@Nullable ILabelProvider toolTipLabelProvide
         this.toolTipLabelProvider = toolTipLabelProvider;
     }
 
-    /**
-     * Configure the given drop down viewer. The given input is used for the viewers input. Clients
-     * must at least set the label and the content provider for the viewer.
-     *
-     * @param viewer the viewer to configure
-     * @param input  the input for the viewer
-     */
-    protected abstract void configureDropDownViewer(@NotNull TreeViewer viewer, @NotNull Object input);
+    @NotNull
+    public ITreeContentProvider getDropDownContentProvider() {
+        return dropDownContentProvider;
+    }
+
+    public void setDropDownContentProvider(@NotNull ITreeContentProvider dropDownContentProvider) {
+        this.dropDownContentProvider = dropDownContentProvider;
+    }
+
+    public int getStyle() {
+        return style;
+    }
 
     private boolean updateSize() {
-        int width = container.getClientArea().width;
+        int containerWidth = container.getClientArea().width;
         int currentWidth = computeWidth();
+
+        if (!COLLAPSE_ELEMENTS) {
+            return currentWidth < containerWidth;
+        }
+
         boolean requiresLayout = false;
 
-        if (currentWidth > width) {
+        if (currentWidth > containerWidth) {
             int index = 0;
-            while (currentWidth > width && index < breadcrumbItems.size()) {
+            while (currentWidth > containerWidth && index < breadcrumbItems.size()) {
                 BreadcrumbItem item = breadcrumbItems.get(index);
                 if (item.isShowText()) {
                     item.setShowText(false);
@@ -232,14 +268,14 @@ private boolean updateSize() {
 
                 index++;
             }
-        } else if (currentWidth < width) {
+        } else if (currentWidth < containerWidth) {
             int index = breadcrumbItems.size() - 1;
-            while (currentWidth < width && index >= 0) {
+            while (currentWidth < containerWidth && index >= 0) {
                 BreadcrumbItem item = breadcrumbItems.get(index);
                 if (!item.isShowText()) {
                     item.setShowText(true);
                     currentWidth = computeWidth();
-                    if (currentWidth > width) {
+                    if (currentWidth > containerWidth) {
                         item.setShowText(false);
                         index = 0;
                     } else {
@@ -276,6 +312,7 @@ private int buildItemChain(@Nullable Object element) {
             item = breadcrumbItems.get(index);
             unmapElement(item.getData());
         } else {
+            ((GridLayout) container.getLayout()).numColumns++;
             item = createItem();
             breadcrumbItems.add(item);
         }


Reviews:
user: login: E1izabeth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-31T16:49:49Z
user: login: ShadelessFox
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-03-31T17:02:03Z
user: login: E1izabeth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-01T09:27:49Z
user: login: E1izabeth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-01T09:28:15Z
user: login: ShadelessFox
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-01T09:58:45Z
user: login: E1izabeth
state: COMMENTED
author_association: MEMBER
submitted_at: 2025-04-01T10:02:16Z
user: login: E1izabeth
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-01T10:18:55Z
user: login: IvanGorshechnikov
state: DISMISSED
author_association: CONTRIBUTOR
submitted_at: 2025-04-01T11:09:57Z
user: login: E1izabeth
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-01T15:24:46Z
user: login: E1izabeth
state: DISMISSED
author_association: MEMBER
submitted_at: 2025-04-01T15:54:28Z
user: login: E1izabeth
state: APPROVED
author_association: MEMBER
submitted_at: 2025-04-01T17:02:13Z

Review comments:
diff_hunk: @@ -292,7 +303,8 @@ protected void performDefaults() {
             UIUtils.setComboSelection(clientTimezone, store.getDefaultString(ModelPreferences.CLIENT_TIMEZONE));
         }
 
-        statusBarShowBreadcrumbsCheck.setSelection(store.getDefaultBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS));
+        statusBarShowBreadcrumbsCheck.setSelection(true);
path: plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageDatabaseUserInterface.java
user: login: E1izabeth
body: Why not getting default from pref store?
created_at: 2025-03-31T16:48:11Z
author_association: MEMBER
diff_hunk: @@ -292,7 +303,8 @@ protected void performDefaults() {
             UIUtils.setComboSelection(clientTimezone, store.getDefaultString(ModelPreferences.CLIENT_TIMEZONE));
         }
 
-        statusBarShowBreadcrumbsCheck.setSelection(store.getDefaultBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS));
+        statusBarShowBreadcrumbsCheck.setSelection(true);
path: plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageDatabaseUserInterface.java
user: login: ShadelessFox
body: Because the reading of that preference is convoluted due to backward compatibility reasons:
https://github.com/dbeaver/dbeaver/blob/e3d1cba3a10f4a5e12cf832b2d0aa19ed4bcf17c/plugins/org.jkiss.dbeaver.ui.editors.base/src/org/jkiss/dbeaver/ui/editors/DatabaseEditorPreferences.java#L33-L41

It would require code duplication.
created_at: 2025-03-31T17:02:03Z
author_association: MEMBER
diff_hunk: @@ -292,7 +303,8 @@ protected void performDefaults() {
             UIUtils.setComboSelection(clientTimezone, store.getDefaultString(ModelPreferences.CLIENT_TIMEZONE));
         }
 
-        statusBarShowBreadcrumbsCheck.setSelection(store.getDefaultBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS));
+        statusBarShowBreadcrumbsCheck.setSelection(true);
path: plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageDatabaseUserInterface.java
user: login: E1izabeth
body: Could you take a look at `SQLCompletionObjectNameFormKind` for example?
created_at: 2025-04-01T09:27:49Z
author_association: MEMBER
diff_hunk: @@ -292,7 +303,8 @@ protected void performDefaults() {
             UIUtils.setComboSelection(clientTimezone, store.getDefaultString(ModelPreferences.CLIENT_TIMEZONE));
         }
 
-        statusBarShowBreadcrumbsCheck.setSelection(store.getDefaultBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS));
+        statusBarShowBreadcrumbsCheck.setSelection(true);
path: plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageDatabaseUserInterface.java
user: login: E1izabeth
body: Alternatively, new state can be stored with the new key
created_at: 2025-04-01T09:28:15Z
author_association: MEMBER
diff_hunk: @@ -292,7 +303,8 @@ protected void performDefaults() {
             UIUtils.setComboSelection(clientTimezone, store.getDefaultString(ModelPreferences.CLIENT_TIMEZONE));
         }
 
-        statusBarShowBreadcrumbsCheck.setSelection(store.getDefaultBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS));
+        statusBarShowBreadcrumbsCheck.setSelection(true);
path: plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageDatabaseUserInterface.java
user: login: ShadelessFox
body: So yeah, it has a separate method for reading the defaults. Fine, I'll add one.

At some point, I really want to get back to https://github.com/dbeaver/pro/issues/2894
created_at: 2025-04-01T09:58:45Z
author_association: MEMBER
diff_hunk: @@ -292,7 +303,8 @@ protected void performDefaults() {
             UIUtils.setComboSelection(clientTimezone, store.getDefaultString(ModelPreferences.CLIENT_TIMEZONE));
         }
 
-        statusBarShowBreadcrumbsCheck.setSelection(store.getDefaultBoolean(DBeaverPreferences.UI_STATUS_BAR_SHOW_BREADCRUMBS));
+        statusBarShowBreadcrumbsCheck.setSelection(true);
path: plugins/org.jkiss.dbeaver.core/src/org/jkiss/dbeaver/ui/preferences/PrefPageDatabaseUserInterface.java
user: login: E1izabeth
body: > At some point, I really want to get back to https://github.com/dbeaver/pro/issues/2894

Yes, I even liked it :)
created_at: 2025-04-01T10:02:16Z
author_association: MEMBER

PR comments:
user: login: Matvey16
created_at: 2025-04-01T14:15:02Z
body: Breadcrumbs in status bar are not shown after dbeaver start (oe properties change) until the focus is changed for the first time. That is not reproducible on earlier versions


https://github.com/user-attachments/assets/3d4a8fca-3cb8-45bf-b4fb-fe5d84bc29ce


author_association: MEMBER
user: login: Matvey16
created_at: 2025-04-01T16:01:15Z
body: Now it is shown in both places on DBeaver start when set to `Object editors`

![image](https://github.com/user-attachments/assets/46ae5f10-e78e-41bb-ac27-792ce7c092e8)

author_association: MEMBER
user: login: Matvey16
created_at: 2025-04-01T16:57:24Z
body: Verified
author_association: MEMBER