feat(ui): move editing context breadcrumb to viewport overlay (#232)
Some checks failed
Build and Test / build (pull_request) Has been cancelled

Convert BreadcrumbToolBar from a QToolBar in the MainWindow toolbar area
to a QFrame overlay at the top-left of each 3D viewport.

Changes:
- BreadcrumbToolBar: change base class from QToolBar to QFrame, use
  QHBoxLayout instead of toolbar actions, semi-transparent overlay
  background with rounded corners
- MainWindow: remove breadcrumb toolbar creation, toolbar break, and
  signal connection
- View3DInventor: create per-view BreadcrumbToolBar as a child of the
  GL viewer widget, positioned at (8,8) with event filter to keep it
  raised on viewport resize

Each 3D view now has its own breadcrumb instance connected to the
EditingContextResolver singleton. This reclaims the full-width toolbar
row and keeps the editing context visually tied to the viewport.
This commit is contained in:
forbes
2026-02-14 19:17:42 -06:00
parent 4cb8a3a1ec
commit fd371573fd
5 changed files with 59 additions and 40 deletions

View File

@@ -26,6 +26,7 @@
#include "EditingContext.h"
#include <QColor>
#include <QHBoxLayout>
#include <QLabel>
#include <QToolButton>
@@ -40,7 +41,6 @@ using namespace Gui;
static QString chipStyle(const QString& bgHex)
{
// Darken background slightly for contrast, use white text
QColor bg(bgHex);
QColor textColor(QStringLiteral("#cdd6f4")); // Catppuccin Text
@@ -81,24 +81,25 @@ static QString separatorStyle()
// ---------------------------------------------------------------------------
BreadcrumbToolBar::BreadcrumbToolBar(QWidget* parent)
: QToolBar(parent)
: QFrame(parent)
{
setObjectName(QStringLiteral("BreadcrumbToolBar"));
setWindowTitle(tr("Editing Context"));
setMovable(false);
setFloatable(false);
setIconSize(QSize(16, 16));
// Thin toolbar with minimal margins
_layout = new QHBoxLayout(this);
_layout->setContentsMargins(6, 4, 6, 4);
_layout->setSpacing(2);
// Semi-transparent overlay background
setStyleSheet(QStringLiteral(
"QToolBar {"
" background: #1e1e2e;" // Catppuccin Base
" border: none;"
" spacing: 2px;"
" padding: 2px 4px;"
" max-height: 24px;"
"QFrame#BreadcrumbToolBar {"
" background-color: rgba(30, 30, 46, 200);" // Catppuccin Base, ~78% opacity
" border: 1px solid rgba(69, 71, 90, 150);" // Surface1
" border-radius: 6px;"
"}"
));
setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Fixed);
setFixedHeight(28);
}
@@ -110,6 +111,7 @@ void BreadcrumbToolBar::updateContext(const EditingContext& ctx)
{
clearSegments();
buildSegments(ctx);
adjustSize();
}
@@ -120,17 +122,16 @@ void BreadcrumbToolBar::updateContext(const EditingContext& ctx)
void BreadcrumbToolBar::clearSegments()
{
for (auto* btn : _segments) {
removeAction(btn->defaultAction());
_layout->removeWidget(btn);
delete btn;
}
_segments.clear();
for (auto* sep : _separators) {
_layout->removeWidget(sep);
delete sep;
}
_separators.clear();
clear();
}
void BreadcrumbToolBar::buildSegments(const EditingContext& ctx)
@@ -144,7 +145,7 @@ void BreadcrumbToolBar::buildSegments(const EditingContext& ctx)
if (i > 0) {
auto* sep = new QLabel(QStringLiteral("\u203A")); // character
sep->setStyleSheet(separatorStyle());
addWidget(sep);
_layout->addWidget(sep);
_separators.append(sep);
}
@@ -168,7 +169,7 @@ void BreadcrumbToolBar::buildSegments(const EditingContext& ctx)
segmentClicked(segmentIndex);
});
addWidget(btn);
_layout->addWidget(btn);
_segments.append(btn);
}
}

View File

@@ -25,10 +25,11 @@
#ifndef GUI_BREADCRUMBTOOLBAR_H
#define GUI_BREADCRUMBTOOLBAR_H
#include <QToolBar>
#include <QFrame>
#include <QList>
#include <FCGlobal.h>
class QHBoxLayout;
class QToolButton;
class QLabel;
@@ -38,11 +39,14 @@ namespace Gui
struct EditingContext;
/**
* A thin toolbar that displays the current editing context as color-coded
* breadcrumb segments. Each segment is a clickable QToolButton; clicking
* a parent segment navigates up (exits the current edit, etc.).
* A small overlay widget that displays the current editing context as
* color-coded breadcrumb segments. Each segment is a clickable QToolButton;
* clicking a parent segment navigates up (exits the current edit, etc.).
*
* Intended to be parented to the 3D viewport widget so it floats at the
* top-left corner of the view.
*/
class GuiExport BreadcrumbToolBar: public QToolBar
class GuiExport BreadcrumbToolBar: public QFrame
{
Q_OBJECT
@@ -59,6 +63,7 @@ private:
void clearSegments();
void buildSegments(const EditingContext& ctx);
QHBoxLayout* _layout;
QList<QToolButton*> _segments;
QList<QLabel*> _separators;
};

View File

@@ -105,8 +105,7 @@
#include "SelectionView.h"
#include "SplashScreen.h"
#include "StatusBarLabel.h"
#include "BreadcrumbToolBar.h"
#include "EditingContext.h"
#include "ToolBarManager.h"
#include "ToolBoxManager.h"
#include "Tree.h"
@@ -307,7 +306,6 @@ struct MainWindowP
QLabel* actionLabel;
InputHintWidget* hintLabel;
QLabel* rightSideLabel;
BreadcrumbToolBar* breadcrumbBar = nullptr;
QTimer* actionTimer;
QTimer* statusTimer;
QTimer* activityTimer;
@@ -492,20 +490,6 @@ MainWindow::MainWindow(QWidget* parent, Qt::WindowFlags f)
#endif
connect(d->mdiArea, &QMdiArea::subWindowActivated, this, &MainWindow::onWindowActivated);
// Breadcrumb toolbar for editing context display
d->breadcrumbBar = new BreadcrumbToolBar(this);
addToolBar(Qt::TopToolBarArea, d->breadcrumbBar);
insertToolBarBreak(d->breadcrumbBar);
// Initialize the editing context resolver and connect to breadcrumb
auto* ctxResolver = EditingContextResolver::instance();
connect(
ctxResolver,
&EditingContextResolver::contextChanged,
d->breadcrumbBar,
&BreadcrumbToolBar::updateContext
);
setupDockWindows();
// accept drops on the window, get handled in dropEvent, dragEnterEvent

View File

@@ -60,7 +60,9 @@
#include "View3DInventor.h"
#include "View3DSettings.h"
#include "Application.h"
#include "BreadcrumbToolBar.h"
#include "BitmapFactory.h"
#include "EditingContext.h"
#include "Camera.h"
#include "Document.h"
#include "FileDialog.h"
@@ -145,6 +147,19 @@ View3DInventor::View3DInventor(
// apply the user settings
applySettings();
// Breadcrumb overlay at top-left of viewport
_breadcrumb = new BreadcrumbToolBar(_viewer->getWidget());
_breadcrumb->move(8, 8);
_breadcrumb->raise();
_breadcrumb->show();
_viewer->getWidget()->installEventFilter(this);
auto* ctxResolver = EditingContextResolver::instance();
connect(ctxResolver, &EditingContextResolver::contextChanged,
_breadcrumb, &BreadcrumbToolBar::updateContext);
// Apply the current context immediately
_breadcrumb->updateContext(ctxResolver->currentContext());
stopSpinTimer = new QTimer(this);
connect(stopSpinTimer, &QTimer::timeout, this, &View3DInventor::stopAnimating);
@@ -935,6 +950,17 @@ void View3DInventor::contextMenuEvent(QContextMenuEvent* e)
MDIView::contextMenuEvent(e);
}
bool View3DInventor::eventFilter(QObject* obj, QEvent* ev)
{
if (obj == _viewer->getWidget() && ev->type() == QEvent::Resize) {
// Keep breadcrumb at top-left after viewport resize
if (_breadcrumb) {
_breadcrumb->raise();
}
}
return MDIView::eventFilter(obj, ev);
}
void View3DInventor::customEvent(QEvent* e)
{
if (e->type() == QEvent::User) {

View File

@@ -40,6 +40,7 @@ class QStackedWidget;
namespace Gui
{
class BreadcrumbToolBar;
class Document;
class View3DInventorViewer;
class View3DPy;
@@ -155,11 +156,13 @@ protected:
void keyPressEvent(QKeyEvent* e) override;
void keyReleaseEvent(QKeyEvent* e) override;
void focusInEvent(QFocusEvent* e) override;
bool eventFilter(QObject* obj, QEvent* ev) override;
void customEvent(QEvent* e) override;
void contextMenuEvent(QContextMenuEvent* e) override;
private:
View3DInventorViewer* _viewer;
BreadcrumbToolBar* _breadcrumb;
PyObject* _viewerPy;
QTimer* stopSpinTimer;
QStackedWidget* stack;