File indexing completed on 2024-04-28 05:50:55
0001 /* 0002 * SPDX-FileCopyrightText: 2019 Tomaz Canabrava <tcanabrava@kde.org> 0003 * 0004 * SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "TerminalHeaderBar.h" 0008 0009 #include "KonsoleSettings.h" 0010 #include "ViewProperties.h" 0011 #include "session/SessionController.h" 0012 #include "terminalDisplay/TerminalDisplay.h" 0013 #include "widgets/ViewSplitter.h" 0014 0015 #include <KLocalizedString> 0016 #include <QApplication> 0017 #include <QBoxLayout> 0018 #include <QDrag> 0019 #include <QLabel> 0020 #include <QMimeData> 0021 #include <QPaintEvent> 0022 #include <QPainter> 0023 #include <QSplitter> 0024 #include <QStyleOptionTabBarBase> 0025 #include <QStylePainter> 0026 #include <QTabBar> 0027 #include <QToolButton> 0028 0029 namespace Konsole 0030 { 0031 TerminalHeaderBar::TerminalHeaderBar(QWidget *parent) 0032 : QWidget(parent) 0033 { 0034 m_boxLayout = new QBoxLayout(QBoxLayout::LeftToRight); 0035 m_boxLayout->setSpacing(0); 0036 m_boxLayout->setContentsMargins(0, 0, 0, 0); 0037 0038 // Session icon 0039 0040 m_terminalIcon = new QLabel(this); 0041 m_terminalIcon->setAlignment(Qt::AlignCenter); 0042 m_terminalIcon->setFixedSize(20, 20); 0043 0044 m_boxLayout->addWidget(m_terminalIcon); 0045 0046 // Status icons 0047 0048 QLabel **statusIcons[] = {&m_statusIconReadOnly, &m_statusIconCopyInput, &m_statusIconSilence, &m_statusIconActivity, &m_statusIconBell}; 0049 0050 for (auto **statusIcon : statusIcons) { 0051 *statusIcon = new QLabel(this); 0052 (*statusIcon)->setAlignment(Qt::AlignCenter); 0053 (*statusIcon)->setFixedSize(20, 20); 0054 (*statusIcon)->setVisible(false); 0055 0056 m_boxLayout->addWidget(*statusIcon); 0057 } 0058 0059 m_statusIconReadOnly->setPixmap(QIcon::fromTheme(QStringLiteral("object-locked")).pixmap(QSize(16, 16))); 0060 m_statusIconCopyInput->setPixmap(QIcon::fromTheme(QStringLiteral("irc-voice")).pixmap(QSize(16, 16))); 0061 m_statusIconSilence->setPixmap(QIcon::fromTheme(QStringLiteral("system-suspend")).pixmap(QSize(16, 16))); 0062 m_statusIconActivity->setPixmap(QIcon::fromTheme(QStringLiteral("dialog-information")).pixmap(QSize(16, 16))); 0063 m_statusIconBell->setPixmap(QIcon::fromTheme(QStringLiteral("notifications")).pixmap(QSize(16, 16))); 0064 0065 // Title 0066 0067 m_terminalTitle = new QLabel(this); 0068 m_terminalTitle->setFont(QApplication::font()); 0069 0070 m_boxLayout->addStretch(); 0071 m_boxLayout->addWidget(m_terminalTitle); 0072 m_boxLayout->addStretch(); 0073 0074 // Expand button 0075 0076 m_toggleExpandedMode = new QToolButton(this); 0077 m_toggleExpandedMode->setIcon(QIcon::fromTheme(QStringLiteral("view-fullscreen"))); // fake 'expand' icon. VDG input? 0078 m_toggleExpandedMode->setAutoRaise(true); 0079 m_toggleExpandedMode->setCheckable(true); 0080 m_toggleExpandedMode->setToolTip(i18nc("@info:tooltip", "Maximize terminal")); 0081 0082 connect(m_toggleExpandedMode, &QToolButton::clicked, this, &TerminalHeaderBar::requestToggleExpansion); 0083 0084 m_boxLayout->addWidget(m_toggleExpandedMode); 0085 0086 // Move to new tab button 0087 0088 m_moveToNewTab = new QToolButton(this); 0089 m_moveToNewTab->setIcon(QIcon::fromTheme(QStringLiteral("tab-new"))); 0090 m_moveToNewTab->setAutoRaise(true); 0091 m_moveToNewTab->setToolTip(i18nc("@info:tooltip", "Move terminal to new tab")); 0092 0093 connect(m_moveToNewTab, &QToolButton::clicked, this, &TerminalHeaderBar::requestMoveToNewTab); 0094 0095 m_boxLayout->addWidget(m_moveToNewTab); 0096 0097 // Close button 0098 0099 m_closeBtn = new QToolButton(this); 0100 m_closeBtn->setIcon(QIcon::fromTheme(QStringLiteral("tab-close"))); 0101 m_closeBtn->setToolTip(i18nc("@info:tooltip", "Close terminal")); 0102 m_closeBtn->setObjectName(QStringLiteral("close-terminal-button")); 0103 m_closeBtn->setAutoRaise(true); 0104 0105 m_boxLayout->addWidget(m_closeBtn); 0106 0107 // The widget itself 0108 0109 setLayout(m_boxLayout); 0110 setAutoFillBackground(true); 0111 setFocusIndicatorState(false); 0112 } 0113 0114 void TerminalHeaderBar::mouseDoubleClickEvent(QMouseEvent *ev) 0115 { 0116 if (ev->button() != Qt::LeftButton) { 0117 return; 0118 } 0119 m_toggleExpandedMode->click(); 0120 } 0121 0122 // Hack until I can detangle the creation of the TerminalViews 0123 void TerminalHeaderBar::finishHeaderSetup(ViewProperties *properties) 0124 { 0125 auto controller = dynamic_cast<SessionController *>(properties); 0126 connect(properties, &Konsole::ViewProperties::titleChanged, this, [this, properties] { 0127 m_terminalTitle->setText(properties->title()); 0128 }); 0129 m_terminalTitle->setText(properties->title()); 0130 0131 connect(properties, &Konsole::ViewProperties::iconChanged, this, [this, properties] { 0132 m_terminalIcon->setPixmap(properties->icon().pixmap(QSize(22, 22))); 0133 }); 0134 m_terminalIcon->setPixmap(properties->icon().pixmap(QSize(22, 22))); 0135 0136 connect(properties, &Konsole::ViewProperties::notificationChanged, this, &Konsole::TerminalHeaderBar::updateNotification); 0137 0138 connect(properties, &Konsole::ViewProperties::readOnlyChanged, this, &Konsole::TerminalHeaderBar::updateSpecialState); 0139 0140 connect(properties, &Konsole::ViewProperties::copyInputChanged, this, &Konsole::TerminalHeaderBar::updateSpecialState); 0141 0142 connect(m_closeBtn, &QToolButton::clicked, controller, &SessionController::closeSession); 0143 } 0144 0145 void TerminalHeaderBar::setFocusIndicatorState(bool focused) 0146 { 0147 m_terminalIsFocused = focused; 0148 update(); 0149 } 0150 0151 void TerminalHeaderBar::updateNotification(ViewProperties *item, Session::Notification notification, bool enabled) 0152 { 0153 Q_UNUSED(item) 0154 0155 switch (notification) { 0156 case Session::Notification::Silence: 0157 m_statusIconSilence->setVisible(enabled); 0158 break; 0159 case Session::Notification::Activity: 0160 m_statusIconActivity->setVisible(enabled); 0161 break; 0162 case Session::Notification::Bell: 0163 m_statusIconBell->setVisible(enabled); 0164 break; 0165 default: 0166 break; 0167 } 0168 } 0169 0170 void TerminalHeaderBar::updateSpecialState(ViewProperties *item) 0171 { 0172 auto controller = dynamic_cast<SessionController *>(item); 0173 0174 if (controller != nullptr) { 0175 m_statusIconReadOnly->setVisible(controller->isReadOnly()); 0176 m_statusIconCopyInput->setVisible(controller->isCopyInputActive()); 0177 } 0178 } 0179 0180 void TerminalHeaderBar::setExpandedMode(bool expand) 0181 { 0182 if (m_toggleExpandedMode->isChecked() != expand) { 0183 m_toggleExpandedMode->setChecked(expand); 0184 } 0185 if (expand) { 0186 m_toggleExpandedMode->setToolTip(i18nc("@info:tooltip", "Restore terminal")); 0187 } else { 0188 m_toggleExpandedMode->setToolTip(i18nc("@info:tooltip", "Maximize terminal")); 0189 } 0190 } 0191 0192 void TerminalHeaderBar::paintEvent(QPaintEvent *paintEvent) 0193 { 0194 /* Try to get the widget that's 10px above this one. 0195 * If the widget is something else than a TerminalWidget, a TabBar or a QSplitter, 0196 * draw a 1px line to separate it from the others. 0197 */ 0198 0199 const auto globalPos = parentWidget()->mapToGlobal(pos()); 0200 auto *widget = qApp->widgetAt(globalPos.x() + 10, globalPos.y() - 10); 0201 0202 const bool isTabbar = qobject_cast<QTabBar *>(widget) != nullptr; 0203 const bool isTerminalWidget = qobject_cast<TerminalDisplay *>(widget) != nullptr; 0204 const bool isSplitter = (qobject_cast<QSplitter *>(widget) != nullptr) || (qobject_cast<QSplitterHandle *>(widget) != nullptr); 0205 if ((widget != nullptr) && !isTabbar && !isTerminalWidget && !isSplitter) { 0206 QStyleOptionTabBarBase optTabBase; 0207 QStylePainter p(this); 0208 optTabBase.initFrom(this); 0209 optTabBase.shape = QTabBar::Shape::RoundedSouth; 0210 optTabBase.documentMode = false; 0211 p.drawPrimitive(QStyle::PE_FrameTabBarBase, optTabBase); 0212 } 0213 0214 QWidget::paintEvent(paintEvent); 0215 if (!m_terminalIsFocused) { 0216 auto p = qApp->palette(); 0217 auto shadowColor = p.color(QPalette::ColorRole::Shadow); 0218 shadowColor.setAlphaF(qreal(0.2) * shadowColor.alphaF()); // same as breeze. 0219 0220 QPainter painter(this); 0221 painter.setPen(Qt::NoPen); 0222 painter.setBrush(shadowColor); 0223 painter.drawRect(rect()); 0224 } 0225 } 0226 0227 void TerminalHeaderBar::mouseMoveEvent(QMouseEvent *ev) 0228 { 0229 if (m_toggleExpandedMode->isChecked()) { 0230 return; 0231 } 0232 auto point = ev->pos() - m_startDrag; 0233 if (point.manhattanLength() > 10) { 0234 auto drag = new QDrag(parent()); 0235 auto mimeData = new QMimeData(); 0236 QByteArray payload; 0237 payload.setNum(qApp->applicationPid()); 0238 mimeData->setData(QStringLiteral("konsole/terminal_display"), payload); 0239 drag->setMimeData(mimeData); 0240 drag->exec(); 0241 } 0242 } 0243 0244 void TerminalHeaderBar::mousePressEvent(QMouseEvent *ev) 0245 { 0246 m_startDrag = ev->pos(); 0247 } 0248 0249 void TerminalHeaderBar::mouseReleaseEvent(QMouseEvent *ev) 0250 { 0251 Q_UNUSED(ev) 0252 } 0253 0254 QSize TerminalHeaderBar::minimumSizeHint() const 0255 { 0256 auto height = sizeHint().height(); 0257 return {height, height}; 0258 } 0259 0260 QSplitter *TerminalHeaderBar::getTopLevelSplitter() 0261 { 0262 QWidget *p = parentWidget(); 0263 // This is expected. 0264 if (qobject_cast<TerminalDisplay *>(p) != nullptr) { 0265 p = p->parentWidget(); 0266 } 0267 0268 // this is also expected. 0269 auto *innerSplitter = qobject_cast<ViewSplitter *>(p); 0270 if (innerSplitter == nullptr) { 0271 return nullptr; 0272 } 0273 0274 return innerSplitter->getToplevelSplitter(); 0275 } 0276 0277 void TerminalHeaderBar::applyVisibilitySettings() 0278 { 0279 auto *settings = KonsoleSettings::self(); 0280 auto toVisibility = settings->splitViewVisibility(); 0281 const bool singleTerminalView = (getTopLevelSplitter()->findChildren<TerminalDisplay *>().count() == 1); 0282 switch (toVisibility) { 0283 case KonsoleSettings::AlwaysShowSplitHeader: 0284 m_toggleExpandedMode->setDisabled(singleTerminalView); 0285 setVisible(true); 0286 break; 0287 case KonsoleSettings::ShowSplitHeaderWhenNeeded: { 0288 const bool visible = !(singleTerminalView); 0289 setVisible(visible); 0290 } break; 0291 case KonsoleSettings::AlwaysHideSplitHeader: 0292 setVisible(false); 0293 default: 0294 break; 0295 } 0296 } 0297 0298 } 0299 0300 #include "moc_TerminalHeaderBar.cpp"