From: Akseli Lahtinen Date: Fri, 4 Apr 2025 20:00:59 +0000 (+0000) Subject: [PATCH] KUrlNavigatorButton: Use arrow as separators X-Git-Tag: archive/raspbian/6.13.0-3+rpi1^2~4 X-Git-Url: https://dgit.raspbian.org/?a=commitdiff_plain;h=911e2436f70c518a7dfe3fbd6944b141a9ec0f45;p=kf6-kio.git [PATCH] KUrlNavigatorButton: Use arrow as separators Due to the feedback of regular separator not being good enough for separating folders, use an arrow separator instead. This also refactors bunch of code around the button, so it should be easier to tweak over time. Remove icons due to the added clutter. They would work with full height chevrons but those are not technically feasible at this time. BUG: 501587 BUG: 501575 BUG: 501582 BUG: 501589 BUG: 501706 BUG: 501803 FIXED-IN: 6.14 Gbp-Pq: Name upstream_KUrlNavigatorButton.patch --- diff --git a/src/filewidgets/kurlnavigator.cpp b/src/filewidgets/kurlnavigator.cpp index 737d039..9c99b1a 100644 --- a/src/filewidgets/kurlnavigator.cpp +++ b/src/filewidgets/kurlnavigator.cpp @@ -199,8 +199,9 @@ public: bool m_active = true; bool m_showPlacesSelector = false; bool m_showFullPath = false; + bool m_backgroundEnabled = true; - int m_padding = 4; + int m_padding = 5; struct { bool showHidden = false; @@ -215,6 +216,8 @@ KUrlNavigatorPrivate::KUrlNavigatorPrivate(const QUrl &url, KUrlNavigator *qq, K { m_layout->setSpacing(0); m_layout->setContentsMargins(0, 0, 0, 0); + QStyleOption option; + option.initFrom(q); q->connect(m_coreUrlNavigator, &KCoreUrlNavigator::currentLocationUrlChanged, q, [this]() { Q_EMIT q->urlChanged(m_coreUrlNavigator->currentLocationUrl()); @@ -306,6 +309,7 @@ KUrlNavigatorPrivate::KUrlNavigatorPrivate(const QUrl &url, KUrlNavigator *qq, K m_layout->addWidget(m_dropDownButton); m_layout->addWidget(m_pathBox, 1); m_layout->addWidget(m_badgeWidgetContainer); + m_layout->addSpacing(q->style()->pixelMetric(QStyle::PM_LayoutHorizontalSpacing, &option, q)); m_layout->addWidget(m_toggleEditableMode); q->setContextMenuPolicy(Qt::CustomContextMenu); @@ -316,7 +320,8 @@ KUrlNavigatorPrivate::KUrlNavigatorPrivate(const QUrl &url, KUrlNavigator *qq, K // Make sure pathBox does not portrude outside of the above frameLineEdit background const int paddingLeft = q->style()->pixelMetric(QStyle::PM_LayoutLeftMargin); const int paddingRight = q->style()->pixelMetric(QStyle::PM_LayoutRightMargin); - q->setContentsMargins(paddingLeft, 0, paddingRight, 0); + q->rect().adjust(0, -1, 0, 1); + q->setContentsMargins(paddingLeft, 1, paddingRight, 1); m_pathBox->setContentsMargins(paddingLeft, 0, paddingRight, 0); } @@ -550,11 +555,14 @@ void KUrlNavigatorPrivate::switchView() m_toggleEditableMode->setChecked(m_editable); updateContent(); if (q->isUrlEditable()) { + m_pathBox->setFixedHeight(m_badgeWidgetContainer->height()); m_pathBox->setFocus(); } q->requestActivation(); Q_EMIT q->editableStateChanged(m_editable); + // Make sure the colors are updated + q->update(); } void KUrlNavigatorPrivate::dropUrls(const QUrl &destination, QDropEvent *event, KUrlNavigatorButton *dropButton) @@ -826,7 +834,7 @@ void KUrlNavigatorPrivate::updateButtonVisibility() // Subtract all widgets from the available width, that must be shown anyway // Make sure to take the padding into account - int availableWidth = q->width() - (m_padding * 2) - m_toggleEditableMode->minimumWidth(); + int availableWidth = q->width() - m_toggleEditableMode->minimumWidth(); availableWidth -= m_badgeWidgetContainer->width(); @@ -838,18 +846,10 @@ void KUrlNavigatorPrivate::updateButtonVisibility() availableWidth -= m_schemes->width(); } - // Check whether buttons must be hidden at all... - int requiredButtonWidth = 0; - for (const auto *button : std::as_const(m_navButtons)) { - requiredButtonWidth += button->minimumWidth(); - } + availableWidth -= m_dropDownButton->width(); - if (requiredButtonWidth > availableWidth) { - // At least one button must be hidden. This implies that the - // drop-down button must get visible, which again decreases the - // available width. - availableWidth -= m_dropDownButton->width(); - } + // Count the paddings of previous button and current button + availableWidth -= m_padding * 4; // Hide buttons... bool isLastButton = true; @@ -904,7 +904,7 @@ void KUrlNavigatorPrivate::updateButtonVisibility() void KUrlNavigatorPrivate::updateTabOrder() { QMultiMap visibleChildrenSortedByX; - const auto childWidgets = q->findChildren(); + const auto childWidgets = q->findChildren(); for (auto childWidget : childWidgets) { if (childWidget->isVisible()) { if (q->layoutDirection() == Qt::LeftToRight) { @@ -1408,22 +1408,40 @@ QWidget *KUrlNavigator::badgeWidget() const } } +void KUrlNavigator::setBackgroundEnabled(bool enabled) +{ + d->m_backgroundEnabled = enabled; +} + +bool KUrlNavigator::isBackgroundEnabled() const +{ + return d->m_backgroundEnabled; +} + void KUrlNavigator::paintEvent(QPaintEvent *event) { Q_UNUSED(event); QPainter painter(this); QStyleOptionFrame option; option.initFrom(this); - option.state = QStyle::State_Sunken; + option.state = QStyle::State_None; if (hasFocus()) { option.palette.setColor(QPalette::Window, palette().color(QPalette::Highlight)); } - // Adjust the rectangle due to how QRect coordinates work - option.rect = option.rect.adjusted(1, 0, -1, 0); - // Draw the background - style()->drawPrimitive(QStyle::PE_FrameLineEdit, &option, &painter, this); + if (d->m_backgroundEnabled) { + // Draw primitive always, but change color if not editable + if (!d->m_editable) { + option.palette.setColor(QPalette::Base, palette().alternateBase().color()); + } + style()->drawPrimitive(QStyle::PE_FrameLineEdit, &option, &painter, this); + } else { + // Draw primitive only for the input field + if (d->m_editable) { + style()->drawPrimitive(QStyle::PE_FrameLineEdit, &option, &painter, this); + } + } } #include "moc_kurlnavigator.cpp" diff --git a/src/filewidgets/kurlnavigator.h b/src/filewidgets/kurlnavigator.h index 7fe7ff7..635cac8 100644 --- a/src/filewidgets/kurlnavigator.h +++ b/src/filewidgets/kurlnavigator.h @@ -332,6 +332,21 @@ public: */ QWidget *badgeWidget() const; + /** + * Sets the background painting enabled or disabled for the buttons layout. + * In frameless styles, its recommended to set the background to disabled. + * Does not affect the input mode. + * @since 6.14 + */ + void setBackgroundEnabled(bool enabled); + + /** + * Returns true if the background of the buttons layout is being painted. + * Does not represent the input mode background. + * @since 6.14 + */ + bool isBackgroundEnabled() const; + public Q_SLOTS: /** * Sets the location to \a url. The old URL is added to the history. diff --git a/src/filewidgets/kurlnavigatorbutton.cpp b/src/filewidgets/kurlnavigatorbutton.cpp index ee104d3..c6bc91a 100644 --- a/src/filewidgets/kurlnavigatorbutton.cpp +++ b/src/filewidgets/kurlnavigatorbutton.cpp @@ -29,7 +29,8 @@ QPointer KUrlNavigatorButton::m_subDirsMenu; KUrlNavigatorButton::KUrlNavigatorButton(const QUrl &url, KUrlNavigator *parent) : KUrlNavigatorButtonBase(parent) - , m_hoverOverIcon(false) + , m_hoverOverArrow(false) + , m_hoverOverButton(false) , m_pendingTextChange(false) , m_replaceButton(false) , m_showMnemonic(false) @@ -39,7 +40,7 @@ KUrlNavigatorButton::KUrlNavigatorButton(const QUrl &url, KUrlNavigator *parent) , m_subDir() , m_openSubDirsTimer(nullptr) , m_subDirsJob(nullptr) - , m_padding(10) + , m_padding(5) { setAcceptDrops(true); setUrl(url); @@ -131,7 +132,7 @@ QSize KUrlNavigatorButton::sizeHint() const // preferred width is textWidth, iconWidth and padding combined // add extra padding in end to make sure the space between divider and button is consistent // the first padding is used between icon and text, second in the end of text - const int width = QFontMetrics(adjustedFont).size(Qt::TextSingleLine, plainText()).width() + iconWidth() + (m_padding * 2); + const int width = m_padding + textWidth() + arrowWidth() + m_padding; return QSize(width, KUrlNavigatorButtonBase::sizeHint().height()); } @@ -172,6 +173,8 @@ void KUrlNavigatorButton::paintEvent(QPaintEvent *event) painter.setFont(adjustedFont); int buttonWidth = width(); + int arrowWidth = KUrlNavigatorButton::arrowWidth(); + int preferredWidth = sizeHint().width(); if (preferredWidth < minimumWidth()) { preferredWidth = minimumWidth(); @@ -180,44 +183,24 @@ void KUrlNavigatorButton::paintEvent(QPaintEvent *event) buttonWidth = preferredWidth; } const int buttonHeight = height(); - const QColor fgColor = foregroundColor(); - drawHoverBackground(&painter); - - int textLeft = 0; - int textWidth = buttonWidth; - const bool leftToRight = (layoutDirection() == Qt::LeftToRight); - // draw folder icon - const int iconW = iconWidth(); - const int iconX = !leftToRight ? (buttonWidth - iconW) - m_padding / 2 : m_padding / 2; - const int iconY = (buttonHeight - iconW) / 2; - - QStyleOption option; - option.initFrom(this); - option.rect = QRect(iconX, iconY, iconW, iconW); - option.palette = palette(); - option.palette.setColor(QPalette::Text, fgColor); - option.palette.setColor(QPalette::WindowText, fgColor); - option.palette.setColor(QPalette::ButtonText, fgColor); - - if (m_hoverOverIcon) { - option.rect = QRect(iconX - m_padding / 2, 0, iconW + m_padding, buttonHeight).marginsRemoved(QMargins(0, 2, 0, 2)); - style()->drawPrimitive(QStyle::PE_PanelButtonTool, &option, &painter, this); - } - - const int widthModifier = iconW + m_padding / 2; - auto pixmap = icon().pixmap(iconSize(), devicePixelRatioF()); - style()->drawItemPixmap(&painter, QRect(iconX, iconY, iconW, iconW), Qt::AlignCenter, pixmap); + // Prepare sizes for icon + QRect textRect; + const int textRectWidth = buttonWidth - arrowWidth - m_padding; if (leftToRight) { - textLeft += widthModifier; + textRect = QRect(m_padding, 0, textRectWidth, buttonHeight); + } else { + // If no separator is drawn, we can start writing text from 0 + textRect = QRect(m_drawSeparator ? arrowWidth : 0, 0, textRectWidth, buttonHeight); } - textWidth -= widthModifier; + drawHoverBackground(&painter); + + // Draw gradient overlay if text is clipped painter.setPen(fgColor); const bool clipped = isTextClipped(); - QRect textRect(textLeft, 0, textWidth, buttonHeight); if (clipped) { QColor bgColor = fgColor; bgColor.setAlpha(0); @@ -237,14 +220,8 @@ void KUrlNavigatorButton::paintEvent(QPaintEvent *event) painter.setPen(pen); } + // Draw folder name int textFlags = Qt::AlignVCenter; - - if (leftToRight) { - textRect.setLeft(textRect.left() + m_padding / 2); - } else { - textRect.setRight(textRect.right() - m_padding / 2); - } - if (m_showMnemonic) { textFlags |= Qt::TextShowMnemonic; painter.drawText(textRect, textFlags, text()); @@ -252,18 +229,26 @@ void KUrlNavigatorButton::paintEvent(QPaintEvent *event) painter.drawText(textRect, textFlags, plainText()); } + // Draw separator arrow if (m_drawSeparator) { QStyleOption option; option.initFrom(this); + option.palette = palette(); + option.palette.setColor(QPalette::Text, fgColor); + option.palette.setColor(QPalette::WindowText, fgColor); + option.palette.setColor(QPalette::ButtonText, fgColor); + if (leftToRight) { - option.rect = QRect(rect().topRight(), rect().bottomRight()); + option.rect = QRect(textRect.right(), 0, arrowWidth, buttonHeight); } else { - option.rect = QRect(rect().topLeft(), rect().bottomLeft()); + // Separator is the first item in RtL mode + option.rect = QRect(0, 0, arrowWidth, buttonHeight); } - // Draw CE_Splitter instead of PE_IndicatorToolBarSeparator, since the latter - // will be turned off if application style has separators turned off - style()->drawControl(QStyle::CE_Splitter, &option, &painter, this); + if (!m_hoverOverArrow) { + option.state = QStyle::State_None; + } + style()->drawPrimitive(leftToRight ? QStyle::PE_IndicatorArrowRight : QStyle::PE_IndicatorArrowLeft, &option, &painter, this); } } @@ -276,6 +261,10 @@ void KUrlNavigatorButton::enterEvent(QEnterEvent *event) if (isTextClipped()) { setToolTip(plainText()); } + if (!m_hoverOverButton) { + m_hoverOverButton = true; + update(); + } } void KUrlNavigatorButton::leaveEvent(QEvent *event) @@ -283,8 +272,12 @@ void KUrlNavigatorButton::leaveEvent(QEvent *event) KUrlNavigatorButtonBase::leaveEvent(event); setToolTip(QString()); - if (m_hoverOverIcon) { - m_hoverOverIcon = false; + if (m_hoverOverArrow) { + m_hoverOverArrow = false; + update(); + } + if (m_hoverOverButton) { + m_hoverOverButton = false; update(); } } @@ -330,8 +323,9 @@ void KUrlNavigatorButton::dragEnterEvent(QDragEnterEvent *event) void KUrlNavigatorButton::dragMoveEvent(QDragMoveEvent *event) { QRect rect = event->answerRect(); - if (isAboveIcon(rect.center().x())) { - m_hoverOverIcon = true; + + if (isAboveSeparator(rect.center().x())) { + m_hoverOverArrow = true; update(); if (m_subDirsMenu == nullptr) { @@ -351,7 +345,7 @@ void KUrlNavigatorButton::dragMoveEvent(QDragMoveEvent *event) m_subDirsMenu->deleteLater(); m_subDirsMenu = nullptr; } - m_hoverOverIcon = false; + m_hoverOverArrow = false; update(); } } @@ -360,14 +354,14 @@ void KUrlNavigatorButton::dragLeaveEvent(QDragLeaveEvent *event) { KUrlNavigatorButtonBase::dragLeaveEvent(event); - m_hoverOverIcon = false; + m_hoverOverArrow = false; setDisplayHintEnabled(DraggedHint, false); update(); } void KUrlNavigatorButton::mousePressEvent(QMouseEvent *event) { - if (isAboveIcon(qRound(event->position().x())) && (event->button() == Qt::LeftButton)) { + if (isAboveSeparator(qRound(event->position().x())) && (event->button() == Qt::LeftButton)) { // the mouse is pressed above the folder button startSubDirsJob(); } @@ -376,7 +370,7 @@ void KUrlNavigatorButton::mousePressEvent(QMouseEvent *event) void KUrlNavigatorButton::mouseReleaseEvent(QMouseEvent *event) { - if (!isAboveIcon(qRound(event->position().x())) || (event->button() != Qt::LeftButton)) { + if (!isAboveSeparator(qRound(event->position().x())) || (event->button() != Qt::LeftButton)) { // the mouse has been released above the text area and not // above the folder button Q_EMIT navigatorButtonActivated(m_url, event->button(), event->modifiers()); @@ -389,9 +383,9 @@ void KUrlNavigatorButton::mouseMoveEvent(QMouseEvent *event) { KUrlNavigatorButtonBase::mouseMoveEvent(event); - const bool hoverOverIcon = isAboveIcon(qRound(event->position().x())); - if (hoverOverIcon != m_hoverOverIcon) { - m_hoverOverIcon = hoverOverIcon; + const bool hoverOverIcon = isAboveSeparator(qRound(event->position().x())); + if (hoverOverIcon != m_hoverOverArrow) { + m_hoverOverArrow = hoverOverIcon; update(); } } @@ -551,7 +545,7 @@ void KUrlNavigatorButton::openSubDirsMenu(KJob *job) initMenu(m_subDirsMenu, 0); const bool leftToRight = (layoutDirection() == Qt::LeftToRight); - const int popupX = !leftToRight ? width() - iconWidth() : 0; + const int popupX = leftToRight ? width() - arrowWidth() : 0; const QPoint popupPos = parentWidget()->mapToGlobal(geometry().bottomLeft() + QPoint(popupX, 0)); QPointer guard(this); @@ -651,28 +645,39 @@ QString KUrlNavigatorButton::plainText() const return dest; } -int KUrlNavigatorButton::iconWidth() const +int KUrlNavigatorButton::arrowWidth() const +{ + // if there isn't arrow then return 0 + int width = 0; + if (!m_subDir.isEmpty()) { + width = height() / 2; + if (width < 4) { + width = 4; + } + } + + return width; +} + +int KUrlNavigatorButton::textWidth() const { - return iconSize().width() * devicePixelRatioF(); + QFont adjustedFont(font()); + adjustedFont.setBold(m_subDir.isEmpty()); + return QFontMetrics(adjustedFont).size(Qt::TextSingleLine, plainText()).width(); } -bool KUrlNavigatorButton::isAboveIcon(int x) const +bool KUrlNavigatorButton::isAboveSeparator(int x) const { const bool leftToRight = (layoutDirection() == Qt::LeftToRight); - return !leftToRight ? (x >= width() - iconWidth() - m_padding) : (x < iconWidth() + m_padding); + return leftToRight ? (x >= width() - arrowWidth()) : (x < arrowWidth() + m_padding); } bool KUrlNavigatorButton::isTextClipped() const { // Ignore padding when resizing, so text doesnt go under it - int availableWidth = width() - m_padding; - if (!m_subDir.isEmpty()) { - availableWidth -= iconWidth(); - } + int availableWidth = width() - arrowWidth() - m_padding; - QFont adjustedFont(font()); - adjustedFont.setBold(m_subDir.isEmpty()); - return QFontMetrics(adjustedFont).size(Qt::TextSingleLine, plainText()).width() >= availableWidth; + return textWidth() >= availableWidth; } void KUrlNavigatorButton::updateMinimumWidth() @@ -680,8 +685,8 @@ void KUrlNavigatorButton::updateMinimumWidth() const int oldMinWidth = minimumWidth(); int minWidth = sizeHint().width(); - if (minWidth < 40) { - minWidth = 40; + if (minWidth < 10) { + minWidth = 10; } else if (minWidth > 150) { // don't let an overlong path name waste all the URL navigator space minWidth = 150; diff --git a/src/filewidgets/kurlnavigatorbutton_p.h b/src/filewidgets/kurlnavigatorbutton_p.h index f10c49d..00bdaf4 100644 --- a/src/filewidgets/kurlnavigatorbutton_p.h +++ b/src/filewidgets/kurlnavigatorbutton_p.h @@ -171,14 +171,16 @@ private: */ QString plainText() const; - int iconWidth() const; - bool isAboveIcon(int x) const; + int arrowWidth() const; + int textWidth() const; + bool isAboveSeparator(int x) const; bool isTextClipped() const; void updateMinimumWidth(); void initMenu(KUrlNavigatorMenu *menu, int startIndex); private: - bool m_hoverOverIcon; + bool m_hoverOverArrow; + bool m_hoverOverButton; bool m_pendingTextChange; bool m_replaceButton; bool m_showMnemonic; diff --git a/src/filewidgets/kurlnavigatorbuttonbase.cpp b/src/filewidgets/kurlnavigatorbuttonbase.cpp index 461d1d9..fe1f0c3 100644 --- a/src/filewidgets/kurlnavigatorbuttonbase.cpp +++ b/src/filewidgets/kurlnavigatorbuttonbase.cpp @@ -1,8 +1,8 @@ /* - SPDX-FileCopyrightText: 2006-2010 Peter Penz - SPDX-FileCopyrightText: 2006 Aaron J. Seigo +SPDX-FileCopyrightText: 2006-2010 Peter Penz +SPDX-FileCopyrightText: 2006 Aaron J. Seigo - SPDX-License-Identifier: LGPL-2.0-or-later +SPDX-License-Identifier: LGPL-2.0-or-later */ #include "kurlnavigatorbuttonbase_p.h" @@ -90,18 +90,14 @@ void KUrlNavigatorButtonBase::drawHoverBackground(QPainter *painter) { const bool isHighlighted = isDisplayHintEnabled(EnteredHint) || isDisplayHintEnabled(DraggedHint) || isDisplayHintEnabled(PopupActiveHint); - QColor backgroundColor = isHighlighted ? palette().color(QPalette::Highlight) : Qt::transparent; - if (!m_active && isHighlighted) { - backgroundColor.setAlpha(128); - } + QStyleOptionButton buttonOption; + buttonOption.initFrom(this); + buttonOption.rect = QRect(0, 0, width(), height()); + buttonOption.features = QStyleOptionButton::Flat; - if (backgroundColor != Qt::transparent) { - // TODO: the backgroundColor should be applied to the style - QStyleOptionViewItem option; - option.initFrom(this); - option.state = QStyle::State_Enabled | QStyle::State_MouseOver; - option.viewItemPosition = QStyleOptionViewItem::OnlyOne; - style()->drawPrimitive(QStyle::PE_PanelItemViewItem, &option, painter, this); + // Draw button graphic + if (isHighlighted) { + style()->drawPrimitive(QStyle::PE_PanelButtonCommand, &buttonOption, painter, this); } } diff --git a/src/filewidgets/kurlnavigatortogglebutton.cpp b/src/filewidgets/kurlnavigatortogglebutton.cpp index 88e45cd..fa0db1f 100644 --- a/src/filewidgets/kurlnavigatortogglebutton.cpp +++ b/src/filewidgets/kurlnavigatortogglebutton.cpp @@ -15,7 +15,7 @@ namespace KDEPrivate { -static constexpr int s_iconSize = KIconLoader::SizeSmallMedium; +static constexpr int s_iconSize = KIconLoader::SizeSmall; KUrlNavigatorToggleButton::KUrlNavigatorToggleButton(KUrlNavigator *parent) : KUrlNavigatorButtonBase(parent)