21 #include <QApplication>
26 #include <QSpacerItem>
29 #include <QMouseEvent>
31 #include <QStackedWidget>
32 #include <QStyleFactory>
33 #include <QStyleOptionTabBarBase>
34 #include <QStylePainter>
36 #include <QToolButton>
39 #include <KAcceleratorManager>
40 #include <KConfigGroup>
41 #include <KLocalizedString>
42 #include <KSharedConfig>
47 #include <KSqueezedTextLabel>
53 class ContainerTabBar :
public QTabBar
58 explicit ContainerTabBar(Container* container)
59 :
QTabBar(container), m_container(container)
77 bool event(
QEvent* ev)
override {
78 if(ev->
type() == QEvent::ToolTip)
82 auto* helpEvent = static_cast<QHelpEvent*>(ev);
83 int tab =
tabAt(helpEvent->pos());
87 m_container->showTooltipForTab(tab);
96 if (event->
button() == Qt::MiddleButton) {
116 if (event->
type() == QEvent::MouseButtonDblClick) {
118 auto mouseEvent = static_cast<const QMouseEvent*>(event);
119 if (mouseEvent->button() == Qt::MiddleButton) {
128 void newTabRequested();
131 Container*
const m_container;
134 bool sortViews(
const View*
const lhs,
const View*
const rhs)
136 return lhs->document()->title().compare(rhs->document()->title(), Qt::CaseInsensitive) < 0;
141 static QMenu* currentDockMenu =
nullptr;
144 class ContainerPrivate
150 ContainerTabBar *tabBar;
152 KSqueezedTextLabel *fileNameCorner;
155 QLabel *shortcutHelpLabel;
157 KSqueezedTextLabel *statusCorner;
160 QMenu* documentListMenu;
170 void updateDocumentListPopupMenu()
172 qDeleteAll(documentListActionForView);
173 documentListActionForView.
clear();
174 documentListMenu->
clear();
180 for (
View* view : qAsConst(viewForWidget)) {
186 for (
int i = 0; i < views.
size(); ++i) {
190 if ((i < views.
size() - 1 && view->
document()->
title() == views.
at(i + 1)->document()->title())
191 || (i > 0 && view->
document()->
title() == views.
at(i - 1)->document()->title())
193 auto urlDoc = qobject_cast<Sublime::UrlDocument*>(view->
document());
198 auto url = urlDoc->url().toString();
200 secondOffset = url.lastIndexOf(
QLatin1Char(
'/'), secondOffset - 1);
201 visibleEntryTitle = url.
right(url.length() - url.lastIndexOf(
QLatin1Char(
'/'), secondOffset) - 1);
208 documentListActionForView[view] = action;
220 if (documentListMenu != currentDockMenu) {
221 documentListMenu->setAsDockMenu();
222 currentDockMenu = documentListMenu;
230 if (documentListMenu == currentDockMenu) {
231 QMenu().setAsDockMenu();
232 currentDockMenu =
nullptr;
238 class UnderlinedLabel:
public KSqueezedTextLabel {
241 explicit UnderlinedLabel(
QTabBar *tabBar,
QWidget* parent =
nullptr)
242 :KSqueezedTextLabel(parent), m_tabBar(tabBar)
253 if (m_tabBar->isVisible() && m_tabBar->count() > 0)
257 optTabBase.
init(m_tabBar);
258 optTabBase.shape = m_tabBar->shape();
259 optTabBase.tabBarRect = m_tabBar->rect();
260 optTabBase.tabBarRect.moveRight(0);
263 tabOverlap.shape = m_tabBar->shape();
264 int overlap = style()->pixelMetric(QStyle::PM_TabBarBaseOverlap, &tabOverlap, m_tabBar);
268 rect.
setRect(0, height()-overlap, width(), overlap);
269 optTabBase.rect = rect;
271 if( m_tabBar->drawBase() )
273 p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase);
278 KSqueezedTextLabel::paintEvent(ev);
285 class StatusLabel:
public UnderlinedLabel {
289 UnderlinedLabel(tabBar, parent)
291 setAlignment(Qt::AlignRight | Qt::AlignVCenter);
292 setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Fixed);
295 QSize minimumSizeHint()
const override
297 QRect rect = style()->itemTextRect(fontMetrics(),
QRect(), Qt::AlignRight,
true, i18n(
"Line: 00000 Col: 000"));
307 , d_ptr(new ContainerPrivate())
311 KAcceleratorManager::setNoAccel(
this);
313 auto *l =
new QBoxLayout(QBoxLayout::TopToBottom,
this);
314 l->setContentsMargins(0, 0, 0, 0);
317 d->layout =
new QBoxLayout(QBoxLayout::LeftToRight);
318 d->layout->setContentsMargins(0, 0, 0, 0);
319 d->layout->setSpacing(0);
321 d->documentListMenu =
new QMenu(
this);
323 d->documentListButton->setIcon(
QIcon::fromTheme(QStringLiteral(
"format-list-unordered")));
324 d->documentListButton->setMenu(d->documentListMenu);
327 setFocusPolicy(Qt::StrongFocus);
329 d->documentListButton->setPopupMode(QToolButton::InstantPopup);
330 d->documentListButton->setAutoRaise(
true);
331 d->documentListButton->setToolTip(i18nc(
"@info:tooltip",
"Show sorted list of opened documents"));
332 d->documentListButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
333 d->layout->addWidget(d->documentListButton);
334 d->tabBar =
new ContainerTabBar(
this);
335 d->tabBar->setContextMenuPolicy(Qt::CustomContextMenu);
336 d->layout->addWidget(d->tabBar);
337 d->fileStatus =
new QLabel(
this );
338 d->fileStatus->setFixedSize(
QSize( 16, 16 ) );
339 d->layout->addWidget(d->fileStatus);
340 d->fileNameCorner =
new UnderlinedLabel(d->tabBar,
this);
341 d->fileNameCorner->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Fixed);
342 d->layout->addWidget(d->fileNameCorner);
343 d->shortcutHelpLabel =
new QLabel(i18nc(
"@info",
"(Press Ctrl+Tab to switch)"),
this);
344 auto font = d->shortcutHelpLabel->font();
345 font.setPointSize(font.pointSize() - 2);
346 font.setItalic(
true);
347 d->shortcutHelpLabel->setFont(font);
348 d->shortCutHelpLeftSpacerItem =
new QSpacerItem(0, 0);
349 d->layout->addSpacerItem(d->shortCutHelpLeftSpacerItem);
350 d->shortcutHelpLabel->setAlignment(Qt::AlignCenter);
351 d->layout->addWidget(d->shortcutHelpLabel);
352 d->shortCutHelpRightSpacerItem =
new QSpacerItem(0, 0);
353 d->layout->addSpacerItem(d->shortCutHelpRightSpacerItem);
354 d->statusCorner =
new StatusLabel(d->tabBar,
this);
355 d->layout->addWidget(d->statusCorner);
356 l->addLayout(d->layout);
359 l->addWidget(d->stack);
361 connect(d->tabBar, &ContainerTabBar::currentChanged,
this, &Container::widgetActivated);
364 connect(d->tabBar, &ContainerTabBar::tabMoved,
this, &Container::tabMoved);
365 connect(d->tabBar, &ContainerTabBar::customContextMenuRequested,
this, &Container::contextMenu);
366 connect(d->tabBar, &ContainerTabBar::tabBarDoubleClicked,
this, &Container::doubleClickTriggered);
367 connect(d->documentListMenu, &
QMenu::triggered,
this, &Container::documentListActionTriggered);
370 setTabBarHidden(!configTabBarVisible());
371 d->tabBar->setTabsClosable(configCloseButtonsOnTabs());
372 d->tabBar->setMovable(
true);
373 d->tabBar->setExpanding(
false);
374 d->tabBar->setSelectionBehaviorOnRemove(QTabBar::SelectPreviousTab);
377 Container::~Container() =
default;
379 bool Container::configTabBarVisible()
381 KConfigGroup group = KSharedConfig::openConfig()->group(
"UiSettings");
382 return group.readEntry(
"TabBarVisibility", 1);
385 bool Container::configCloseButtonsOnTabs()
387 KConfigGroup group = KSharedConfig::openConfig()->group(
"UiSettings");
388 return group.readEntry(
"CloseButtonsOnTabs", 1);
391 void Container::setLeftCornerWidget(
QWidget* widget)
395 if(d->leftCornerWidget.data() == widget) {
396 if(d->leftCornerWidget)
397 d->leftCornerWidget.data()->setParent(
nullptr);
399 delete d->leftCornerWidget.data();
400 d->leftCornerWidget.clear();
402 d->leftCornerWidget = widget;
405 widget->
setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Preferred);
406 d->layout->insertWidget(0, widget);
414 return d->viewForWidget.values();
417 void Container::requestClose(
int idx)
419 emit requestClose(widget(idx));
422 void Container::widgetActivated(
int idx)
428 if (
QWidget* w = d->stack->widget(idx)) {
431 emit activateView(view);
435 void Container::addWidget(View *view,
int position)
439 QWidget *w = view->widget(
this);
443 idx = d->stack->insertWidget(position, w);
446 idx = d->stack->addWidget(w);
447 d->tabBar->insertTab(idx, view->document()->statusIcon(), view->document()->title());
449 d->viewForWidget[w] = view;
454 d->updateDocumentListPopupMenu();
456 setCurrentWidget(d->stack->currentWidget());
461 d->tabBar->setMinimumHeight(d->tabBar->sizeHint().height());
463 connect(view, &View::statusChanged,
this, &Container::statusChanged);
464 connect(view->document(), &Document::statusIconChanged,
this, &Container::statusIconChanged);
465 connect(view->document(), &Document::titleChanged,
this, &Container::documentTitleChanged);
473 d->statusCorner->setText(statusText);
474 d->statusCorner->setVisible(!statusText.isEmpty());
477 void Container::statusIconChanged(Document* doc)
483 if (it.
next().value()->document() == doc) {
484 d->fileStatus->setPixmap( doc->statusIcon().pixmap(
QSize( 16,16 ) ) );
485 int tabIndex = d->stack->indexOf(it.
key());
486 if (tabIndex != -1) {
487 d->tabBar->setTabIcon(tabIndex, doc->statusIcon());
492 Q_ASSERT(d->documentListActionForView.contains(it.
value()));
493 d->documentListActionForView[it.
value()]->setIcon(doc->icon());
507 if (currentView() == view) {
508 d->fileNameCorner->setText( doc->
title(Document::Extended) );
512 d->fileNameCorner->updateGeometry();
514 int tabIndex = d->stack->indexOf(it.
key());
515 if (tabIndex != -1) {
516 d->tabBar->setTabText(tabIndex, doc->
title());
522 d->updateDocumentListPopupMenu();
525 int Container::count()
const
527 Q_D(
const Container);
529 return d->stack->count();
532 QWidget* Container::currentWidget()
const
534 Q_D(
const Container);
536 return d->stack->currentWidget();
539 void Container::setCurrentWidget(
QWidget* w)
543 if (d->stack->currentWidget() == w) {
546 d->stack->setCurrentWidget(w);
547 d->tabBar->setCurrentIndex(d->stack->indexOf(w));
548 if (
View* view = viewForWidget(w))
551 if (!d->tabBar->isVisible())
556 documentTitleChanged( view->
document() );
561 QWidget* Container::widget(
int i)
const
563 Q_D(
const Container);
565 return d->stack->widget(i);
568 int Container::indexOf(
QWidget* w)
const
570 Q_D(
const Container);
572 return d->stack->indexOf(w);
575 void Container::removeWidget(
QWidget *w)
580 int widgetIdx = d->stack->indexOf(w);
581 d->stack->removeWidget(w);
582 d->tabBar->removeTab(widgetIdx);
583 if (d->tabBar->currentIndex() != -1 && !d->tabBar->isVisible()) {
586 View* view = currentView();
588 statusIconChanged( view->
document() );
589 documentTitleChanged( view->
document() );
592 View* view = d->viewForWidget.take(w);
595 disconnect(view->
document(), &Document::titleChanged,
this, &Container::documentTitleChanged);
596 disconnect(view->
document(), &Document::statusIconChanged,
this, &Container::statusIconChanged);
597 disconnect(view, &View::statusChanged,
this, &Container::statusChanged);
600 Q_ASSERT(d->documentListActionForView.contains(view));
601 delete d->documentListActionForView.take(view);
606 bool Container::hasWidget(
QWidget* w)
const
608 Q_D(
const Container);
610 return d->stack->indexOf(w) != -1;
613 View *Container::viewForWidget(
QWidget *w)
const
615 Q_D(
const Container);
617 return d->viewForWidget.value(w);
620 void Container::setTabBarHidden(
bool hide)
627 d->fileStatus->show();
628 d->shortCutHelpLeftSpacerItem->changeSize(style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing), 0,
629 QSizePolicy::Fixed, QSizePolicy::Fixed);
630 d->shortcutHelpLabel->show();
631 d->shortCutHelpRightSpacerItem->changeSize(0, 0, QSizePolicy::Expanding, QSizePolicy::Minimum);
632 d->fileNameCorner->show();
636 d->fileNameCorner->hide();
637 d->fileStatus->hide();
639 d->shortCutHelpLeftSpacerItem->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
640 d->shortcutHelpLabel->hide();
641 d->shortCutHelpRightSpacerItem->changeSize(0, 0, QSizePolicy::Fixed, QSizePolicy::Fixed);
644 d->layout->invalidate();
646 View* v = currentView();
648 documentTitleChanged(v->
document());
652 void Container::setCloseButtonsOnTabs(
bool show)
656 d->tabBar->setTabsClosable(show);
659 void Container::resetTabColors(
const QColor& color)
663 for (
int i = 0; i < count(); i++){
664 d->tabBar->setTabTextColor(i, color);
668 void Container::setTabColor(
const View* view,
const QColor& color)
672 for (
int i = 0; i < count(); i++){
673 if (view == viewForWidget(widget(i))) {
674 d->tabBar->setTabTextColor(i, color);
683 for (
int i = 0; i < count(); i++) {
684 auto view = viewForWidget(widget(i));
685 auto color = colors[view];
687 d->tabBar->setTabTextColor(i, color);
692 void Container::tabMoved(
int from,
int to)
696 QWidget *w = d->stack->widget(from);
697 d->stack->removeWidget(w);
698 d->stack->insertWidget(to, w);
699 d->viewForWidget[w]->notifyPositionChanged(to);
702 void Container::contextMenu(
const QPoint& pos )
706 QWidget* senderWidget = qobject_cast<QWidget*>(sender());
707 Q_ASSERT(senderWidget);
709 int currentTab = d->tabBar->tabAt(pos);
721 auto parentWindowHandle = senderWidget->windowHandle();
722 if (!parentWindowHandle) {
725 menu.windowHandle()->setTransientParent(parentWindowHandle);
728 emit tabContextMenuRequested(view, &menu);
731 QAction* copyPathAction =
nullptr;
732 QAction* closeTabAction =
nullptr;
733 QAction* closeOtherTabsAction =
nullptr;
736 i18nc(
"@action:inmenu",
"Copy Filename"));
739 i18nc(
"@action:inmenu",
"Close"));
741 i18nc(
"@action:inmenu",
"Close All Other"));
748 if ( triggered == closeTabAction ) {
749 requestClose(currentTab);
750 }
else if ( triggered == closeOtherTabsAction ) {
752 widgetActivated(currentTab);
755 for (
int i = 0; i < count(); ++i ) {
756 if ( i != currentTab ) {
757 otherTabs << widget(i);
761 for (
QWidget* tab : qAsConst(otherTabs)) {
762 emit requestClose(tab);
764 }
else if ( triggered == closeAllTabsAction ) {
766 widgetActivated(count() - 1);
769 for (
int i = 0; i < count(); ++i ) {
770 emit requestClose(widget(i));
772 }
else if( triggered == copyPathAction ) {
773 auto view = viewForWidget( widget( currentTab ) );
774 auto urlDocument = qobject_cast<UrlDocument*>( view->
document() );
776 QString toCopy = urlDocument->url().toDisplayString(QUrl::PreferLocalFile);
777 if (urlDocument->url().isLocalFile()) {
786 void Container::showTooltipForTab(
int tab)
788 emit tabToolTipRequested(viewForWidget(widget(tab)),
this, tab);
791 bool Container::isCurrentTab(
int tab)
const
793 Q_D(
const Container);
795 return d->tabBar->currentIndex() == tab;
798 QRect Container::tabRect(
int tab)
const
800 Q_D(
const Container);
802 return d->tabBar->tabRect(tab).translated(d->tabBar->mapToGlobal(
QPoint(0, 0)));
805 void Container::doubleClickTriggered(
int tab)
808 emit newTabRequested();
810 emit tabDoubleClicked(viewForWidget(widget(tab)));
814 void Container::documentListActionTriggered(
QAction* action)
820 QWidget* widget = d->viewForWidget.key(view);
822 setCurrentWidget(widget);
828 return d->viewForWidget.value(widget( d->tabBar->currentIndex() ));
841 #include "container.moc"