File indexing completed on 2024-05-19 04:59:21
0001 /* ============================================================ 0002 * VerticalTabs plugin for Falkon 0003 * Copyright (C) 2018 David Rosca <nowrep@gmail.com> 0004 * 0005 * This program is free software: you can redistribute it and/or modify 0006 * it under the terms of the GNU General Public License as published by 0007 * the Free Software Foundation, either version 3 of the License, or 0008 * (at your option) any later version. 0009 * 0010 * This program is distributed in the hope that it will be useful, 0011 * but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0013 * GNU General Public License for more details. 0014 * 0015 * You should have received a copy of the GNU General Public License 0016 * along with this program. If not, see <http://www.gnu.org/licenses/>. 0017 * ============================================================ */ 0018 #include "tabtreedelegate.h" 0019 #include "tabtreeview.h" 0020 #include "loadinganimator.h" 0021 0022 #include "tabmodel.h" 0023 #include "tabicon.h" 0024 0025 #include <QTabBar> 0026 #include <QPainter> 0027 #include <QPushButton> 0028 #include <QApplication> 0029 0030 // TabTreeCloseButton 0031 TabTreeCloseButton::TabTreeCloseButton(QWidget *parent) 0032 : QAbstractButton(parent) 0033 { 0034 int width = style()->pixelMetric(QStyle::PM_TabCloseIndicatorWidth, nullptr, this); 0035 int height = style()->pixelMetric(QStyle::PM_TabCloseIndicatorHeight, nullptr, this); 0036 resize(width, height); 0037 } 0038 0039 int TabTreeCloseButton::showOnNormal() const 0040 { 0041 return m_showOnNormal; 0042 } 0043 0044 void TabTreeCloseButton::setShowOnNormal(int show) 0045 { 0046 m_showOnNormal = show; 0047 } 0048 0049 int TabTreeCloseButton::showOnHovered() const 0050 { 0051 return m_showOnHovered; 0052 } 0053 0054 void TabTreeCloseButton::setShowOnHovered(int show) 0055 { 0056 m_showOnHovered = show; 0057 } 0058 0059 int TabTreeCloseButton::showOnSelected() const 0060 { 0061 return m_showOnSelected; 0062 } 0063 0064 void TabTreeCloseButton::setShowOnSelected(int show) 0065 { 0066 m_showOnSelected = show; 0067 } 0068 0069 bool TabTreeCloseButton::isVisible(bool hovered, bool selected) const 0070 { 0071 if (hovered && selected) { 0072 return m_showOnHovered || m_showOnSelected; 0073 } else if (selected) { 0074 return m_showOnSelected; 0075 } else if (hovered) { 0076 return m_showOnHovered; 0077 } else { 0078 return m_showOnNormal; 0079 } 0080 } 0081 0082 void TabTreeCloseButton::paintEvent(QPaintEvent *) 0083 { 0084 } 0085 0086 // TabTreeDelegate 0087 TabTreeDelegate::TabTreeDelegate(TabTreeView *view) 0088 : QStyledItemDelegate() 0089 , m_view(view) 0090 { 0091 m_padding = qMax(5, m_view->style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1); 0092 m_indentation = 15; 0093 0094 m_loadingAnimator = new LoadingAnimator(this); 0095 connect(m_loadingAnimator, &LoadingAnimator::updateIndex, m_view, &TabTreeView::updateIndex); 0096 0097 // Needed to make it stylable the same way as real tabbar close button 0098 auto *tabBar = new QTabBar(m_view); 0099 tabBar->setObjectName(QSL("tabtree_tabbar")); 0100 tabBar->lower(); 0101 0102 m_closeButton = new TabTreeCloseButton(tabBar); 0103 m_closeButton->lower(); 0104 } 0105 0106 static int indexDepth(QModelIndex index) 0107 { 0108 int i = 0; 0109 while (index.parent().isValid()) { 0110 index = index.parent(); 0111 i++; 0112 } 0113 return i; 0114 } 0115 0116 QRect TabTreeDelegate::expandButtonRect(const QModelIndex &index) const 0117 { 0118 const QRect rect = m_view->visualRect(index); 0119 const int depth = indexDepth(index); 0120 return QRect(m_indentation * depth, rect.y(), m_indentation, rect.height()); 0121 } 0122 0123 QRect TabTreeDelegate::audioButtonRect(const QModelIndex &index) const 0124 { 0125 if (!index.data(TabModel::AudioPlayingRole).toBool() && !index.data(TabModel::AudioMutedRole).toBool()) { 0126 return QRect(); 0127 } 0128 const QRect rect = m_view->visualRect(index); 0129 const int center = rect.height() / 2 + rect.top(); 0130 const int rightPosition = rect.right() - m_padding * 2 - 16; 0131 return QRect(rightPosition - 16, center - 16 / 2, 16, 16); 0132 } 0133 0134 QRect TabTreeDelegate::closeButtonRect(const QModelIndex &index) const 0135 { 0136 const QRect rect = m_view->visualRect(index); 0137 const int center = rect.height() / 2 + rect.top(); 0138 QSize size = m_closeButton->size(); 0139 size.setHeight(qMin(rect.height() - m_padding, size.height())); 0140 return QRect(QPoint(rect.right() - m_padding - size.width(), center - size.height() / 2), size); 0141 } 0142 0143 void TabTreeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const 0144 { 0145 const QWidget *w = option.widget; 0146 const QStyle *style = w ? w->style() : m_view->style(); 0147 0148 const bool expanded = m_view->isExpanded(index); 0149 const bool children = m_view->model()->rowCount(index) > 0; 0150 const int depth = indexDepth(index); 0151 const bool isRestoredTab = index.data(TabModel::RestoredRole).toBool(); 0152 0153 QStyleOptionViewItem opt = option; 0154 initStyleOption(&opt, index); 0155 m_view->adjustStyleOption(&opt); 0156 0157 const int height = opt.rect.height(); 0158 const int center = height / 2 + opt.rect.top(); 0159 0160 int leftPosition = opt.rect.left() + m_indentation + m_indentation * depth + m_padding; 0161 int rightPosition = opt.rect.right() - m_padding * 2 - m_closeButton->size().width(); 0162 0163 const QPalette::ColorRole colorRole = opt.state & QStyle::State_Selected ? QPalette::HighlightedText : QPalette::Text; 0164 0165 QPalette::ColorGroup cg = opt.state & QStyle::State_Enabled ? QPalette::Normal : QPalette::Disabled; 0166 if (!isRestoredTab) { 0167 cg = QPalette::Disabled; 0168 } 0169 if (cg == QPalette::Normal && !(opt.state & QStyle::State_Active)) { 0170 cg = QPalette::Inactive; 0171 } 0172 0173 #ifdef Q_OS_WIN 0174 opt.palette.setColor(QPalette::All, QPalette::HighlightedText, opt.palette.color(QPalette::Active, QPalette::Text)); 0175 opt.palette.setColor(QPalette::All, QPalette::Highlight, opt.palette.base().color().darker(108)); 0176 #endif 0177 0178 QPalette textPalette = opt.palette; 0179 textPalette.setCurrentColorGroup(cg); 0180 0181 const bool hovered = opt.state.testFlag(QStyle::State_MouseOver); 0182 const bool selected = opt.state.testFlag(QStyle::State_Selected); 0183 0184 // Draw background 0185 if (m_view->backgroundIndentation()) { 0186 opt.rect.moveLeft(m_indentation * depth); 0187 opt.rect.setWidth(opt.rect.width() - m_indentation * depth); 0188 } 0189 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, w); 0190 0191 // Draw expand button 0192 if (children) { 0193 QStyleOptionViewItem o = opt; 0194 o.state &= ~QStyle::State_MouseOver; 0195 o.rect.moveLeft(m_indentation * depth); 0196 o.rect.setWidth(m_indentation); 0197 style->drawPrimitive(expanded ? QStyle::PE_IndicatorArrowDown : QStyle::PE_IndicatorArrowRight, &o, painter, w); 0198 } 0199 0200 // Draw icon 0201 const int iconSize = 16; 0202 const int iconYPos = center - (iconSize / 2); 0203 QRect iconRect(leftPosition, iconYPos, iconSize, iconSize); 0204 QPixmap pixmap; 0205 if (index.data(TabModel::LoadingRole).toBool()) { 0206 pixmap = m_loadingAnimator->pixmap(index); 0207 } else { 0208 pixmap = index.data(Qt::DecorationRole).value<QIcon>().pixmap(iconSize); 0209 } 0210 painter->drawPixmap(iconRect, pixmap); 0211 leftPosition += iconRect.width() + m_padding; 0212 0213 // Draw close button 0214 if (m_closeButton->isVisible(hovered, selected)) { 0215 QStyleOptionButton o; 0216 o.initFrom(m_closeButton); 0217 0218 const bool hovered = closeButtonRect(index).contains(m_view->viewport()->mapFromGlobal(QCursor::pos())); 0219 const bool pressed = hovered && QApplication::mouseButtons() == Qt::LeftButton; 0220 0221 QSize closeSize = QSize(o.rect.size().width(), qMin(height - m_padding, o.rect.size().height())); 0222 QPoint pos(opt.rect.right() - m_padding - closeSize.width(), center - closeSize.height() / 2); 0223 o.rect = QRect(pos, closeSize); 0224 o.state |= QStyle::State_AutoRaise | QStyle::State_Enabled | QStyle::State_Selected; 0225 o.state.setFlag(QStyle::State_Raised, hovered && !pressed); 0226 o.state.setFlag(QStyle::State_Sunken, pressed); 0227 o.state.setFlag(QStyle::State_MouseOver, hovered); 0228 style->drawPrimitive(QStyle::PE_IndicatorTabClose, &o, painter, m_closeButton); 0229 } 0230 0231 // Draw audio icon 0232 const bool audioMuted = index.data(TabModel::AudioMutedRole).toBool(); 0233 const bool audioPlaying = index.data(TabModel::AudioPlayingRole).toBool(); 0234 if (audioMuted || audioPlaying) { 0235 QSize audioSize(16, 16); 0236 QPoint pos(rightPosition - audioSize.width(), center - audioSize.height() / 2); 0237 QRect audioRect(pos, audioSize); 0238 painter->drawPixmap(audioRect, audioMuted ? TabIcon::data()->audioMutedPixmap : TabIcon::data()->audioPlayingPixmap); 0239 rightPosition -= audioSize.width() + m_padding; 0240 } 0241 0242 // Draw title 0243 QRect titleRect(leftPosition, center - opt.fontMetrics.height() / 2, opt.rect.width(), opt.fontMetrics.height()); 0244 titleRect.setRight(rightPosition - m_padding); 0245 QString title = opt.fontMetrics.elidedText(index.data().toString(), Qt::ElideRight, titleRect.width()); 0246 style->drawItemText(painter, titleRect, Qt::AlignLeft, textPalette, true, title, colorRole); 0247 } 0248 0249 QSize TabTreeDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const 0250 { 0251 QStyleOptionViewItem opt(option); 0252 initStyleOption(&opt, index); 0253 0254 return QSize(200, m_padding * 2 + opt.fontMetrics.height()); 0255 }