File indexing completed on 2024-11-10 04:56:51

0001 /*
0002     KWin - the KDE window manager
0003     This file is part of the KDE project.
0004 
0005     SPDX-FileCopyrightText: 2008 Lubos Lunak <l.lunak@suse.cz>
0006     SPDX-FileCopyrightText: 2009 Lucas Murray <lmurray@undefinedfire.com>
0007 
0008     SPDX-License-Identifier: GPL-2.0-or-later
0009 */
0010 
0011 #include "monitor.h"
0012 
0013 #include <KLocalizedString>
0014 #include <KSvg/FrameSvg>
0015 
0016 #include <QApplication>
0017 #include <QDebug>
0018 #include <QGraphicsScene>
0019 #include <QGraphicsSceneEvent>
0020 #include <QGraphicsView>
0021 #include <QMenu>
0022 #include <QScreen>
0023 #include <QWindow>
0024 
0025 namespace KWin
0026 {
0027 
0028 static QScreen *screenFromWidget(const QWidget *widget)
0029 {
0030     QScreen *screen = widget->screen();
0031     if (screen) {
0032         return screen;
0033     }
0034 
0035     return QGuiApplication::primaryScreen();
0036 }
0037 
0038 Monitor::Monitor(QWidget *parent)
0039     : ScreenPreviewWidget(parent)
0040 {
0041     for (auto &popup : m_popups) {
0042         popup = std::make_unique<QMenu>(this);
0043     }
0044     m_scene = std::make_unique<QGraphicsScene>(this);
0045     m_view = std::make_unique<QGraphicsView>(m_scene.get(), this);
0046     m_view->setBackgroundBrush(Qt::black);
0047     m_view->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0048     m_view->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
0049     m_view->setFocusPolicy(Qt::NoFocus);
0050     m_view->setFrameShape(QFrame::NoFrame);
0051     for (size_t i = 0; i < m_items.size(); i++) {
0052         m_items[i] = std::make_unique<Corner>(this);
0053         m_scene->addItem(m_items[i].get());
0054         m_hidden[i] = false;
0055         m_actionGroups[i] = std::make_unique<QActionGroup>(this);
0056     }
0057     QRect avail = screenFromWidget(this)->geometry();
0058     setMinimumContentWidth(20 * 3 + 5 * 2); // 3 buttons in a row and some spacing between them
0059     setRatio((qreal)avail.width() / (qreal)avail.height());
0060     checkSize();
0061 }
0062 
0063 Monitor::~Monitor() = default;
0064 
0065 void Monitor::clear()
0066 {
0067     for (size_t i = 0; i < m_popups.size(); i++) {
0068         m_popups[i]->clear();
0069         m_items[i]->setActive(false);
0070         setEdgeHidden(i, false);
0071         m_actionGroups[i] = std::make_unique<QActionGroup>(this);
0072     }
0073 }
0074 
0075 void Monitor::resizeEvent(QResizeEvent *e)
0076 {
0077     ScreenPreviewWidget::resizeEvent(e);
0078     checkSize();
0079 }
0080 
0081 bool Monitor::event(QEvent *event)
0082 {
0083     const bool r = ScreenPreviewWidget::event(event);
0084     if (event->type() == QEvent::ScreenChangeInternal) {
0085         QRect avail = screenFromWidget(this)->geometry();
0086         setRatio((qreal)avail.width() / (qreal)avail.height());
0087         checkSize();
0088     }
0089     return r;
0090 }
0091 
0092 void Monitor::checkSize()
0093 {
0094     QRect contentsRect = previewRect();
0095     m_view->setGeometry(contentsRect);
0096     m_scene->setSceneRect(QRect(QPoint(0, 0), contentsRect.size()));
0097     const int x2 = (contentsRect.width() - 20) / 2;
0098     const int x3 = contentsRect.width() - 20;
0099     const int y2 = (contentsRect.height() - 20) / 2;
0100     const int y3 = contentsRect.height() - 20;
0101     m_items[0]->setRect(0, y2, 20, 20);
0102     m_items[1]->setRect(x3, y2, 20, 20);
0103     m_items[2]->setRect(x2, 0, 20, 20);
0104     m_items[3]->setRect(x2, y3, 20, 20);
0105     m_items[4]->setRect(0, 0, 20, 20);
0106     m_items[5]->setRect(x3, 0, 20, 20);
0107     m_items[6]->setRect(0, y3, 20, 20);
0108     m_items[7]->setRect(x3, y3, 20, 20);
0109 }
0110 
0111 void Monitor::setEdgeEnabled(int edge, bool enabled)
0112 {
0113     for (QAction *action : std::as_const(m_popupActions[edge])) {
0114         action->setEnabled(enabled);
0115     }
0116 }
0117 
0118 void Monitor::setEdgeHidden(int edge, bool set)
0119 {
0120     m_hidden[edge] = set;
0121     if (set) {
0122         m_items[edge]->hide();
0123     } else {
0124         m_items[edge]->show();
0125     }
0126 }
0127 
0128 bool Monitor::edgeHidden(int edge) const
0129 {
0130     return m_hidden[edge];
0131 }
0132 
0133 void Monitor::addEdgeItem(int edge, const QString &item)
0134 {
0135     QAction *act = m_popups[edge]->addAction(item);
0136     act->setCheckable(true);
0137     m_popupActions[edge].append(act);
0138     m_actionGroups[edge]->addAction(act);
0139     if (m_popupActions[edge].count() == 1) {
0140         act->setChecked(true);
0141         m_items[edge]->setToolTip(item);
0142     }
0143     m_items[edge]->setActive(!m_popupActions[edge].front()->isChecked());
0144 }
0145 
0146 void Monitor::setEdgeItemEnabled(int edge, int index, bool enabled)
0147 {
0148     m_popupActions[edge][index]->setEnabled(enabled);
0149 }
0150 
0151 bool Monitor::edgeItemEnabled(int edge, int index) const
0152 {
0153     return m_popupActions[edge][index]->isEnabled();
0154 }
0155 
0156 void Monitor::selectEdgeItem(int edge, int index)
0157 {
0158     m_popupActions[edge][index]->setChecked(true);
0159     m_items[edge]->setActive(!m_popupActions[edge].front()->isChecked());
0160     QString actionText = m_popupActions[edge][index]->text();
0161     // remove accelerators added by KAcceleratorManager
0162     actionText = KLocalizedString::removeAcceleratorMarker(actionText);
0163     m_items[edge]->setToolTip(actionText);
0164 }
0165 
0166 int Monitor::selectedEdgeItem(int edge) const
0167 {
0168     const auto &actions = m_popupActions[edge];
0169     for (QAction *act : actions) {
0170         if (act->isChecked()) {
0171             return actions.indexOf(act);
0172         }
0173     }
0174     Q_UNREACHABLE();
0175 }
0176 
0177 void Monitor::popup(Corner *c, QPoint pos)
0178 {
0179     for (size_t i = 0; i < m_items.size(); i++) {
0180         if (m_items[i].get() == c) {
0181             if (m_popupActions[i].empty()) {
0182                 return;
0183             }
0184             if (QAction *a = m_popups[i]->exec(pos)) {
0185                 selectEdgeItem(i, m_popupActions[i].indexOf(a));
0186                 Q_EMIT changed();
0187                 Q_EMIT edgeSelectionChanged(i, m_popupActions[i].indexOf(a));
0188                 c->setToolTip(KLocalizedString::removeAcceleratorMarker(a->text()));
0189             }
0190             return;
0191         }
0192     }
0193     Q_UNREACHABLE();
0194 }
0195 
0196 void Monitor::flip(Corner *c, QPoint pos)
0197 {
0198     for (size_t i = 0; i < m_items.size(); i++) {
0199         if (m_items[i].get() == c) {
0200             if (m_popupActions[i].empty()) {
0201                 m_items[i]->setActive(m_items[i]->brush() != Qt::green);
0202             } else {
0203                 popup(c, pos);
0204             }
0205             return;
0206         }
0207     }
0208     Q_UNREACHABLE();
0209 }
0210 
0211 Monitor::Corner::Corner(Monitor *m)
0212     : m_monitor(m)
0213     , m_button(std::make_unique<KSvg::FrameSvg>())
0214 {
0215     m_button->setImageSet(m->svgImageSet());
0216     m_button->setImagePath("widgets/button");
0217     setAcceptHoverEvents(true);
0218 }
0219 
0220 Monitor::Corner::~Corner() = default;
0221 
0222 void Monitor::Corner::contextMenuEvent(QGraphicsSceneContextMenuEvent *e)
0223 {
0224     m_monitor->popup(this, e->screenPos());
0225 }
0226 
0227 void Monitor::Corner::mousePressEvent(QGraphicsSceneMouseEvent *e)
0228 {
0229     m_monitor->flip(this, e->screenPos());
0230 }
0231 
0232 void Monitor::Corner::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
0233 {
0234     if (m_hover) {
0235         m_button->setElementPrefix("normal");
0236 
0237         qreal left, top, right, bottom;
0238         m_button->getMargins(left, top, right, bottom);
0239 
0240         m_button->setElementPrefix("active");
0241         qreal activeLeft, activeTop, activeRight, activeBottom;
0242         m_button->getMargins(activeLeft, activeTop, activeRight, activeBottom);
0243 
0244         QRectF activeRect = QRectF(QPointF(0, 0), rect().size());
0245         activeRect.adjust(left - activeLeft, top - activeTop,
0246                           -(right - activeRight), -(bottom - activeBottom));
0247         m_button->setElementPrefix("active");
0248         m_button->resizeFrame(activeRect.size());
0249         m_button->paintFrame(painter, rect().topLeft() + activeRect.topLeft());
0250     } else {
0251         m_button->setElementPrefix(m_active ? "pressed" : "normal");
0252         m_button->resizeFrame(rect().size());
0253         m_button->paintFrame(painter, rect().topLeft());
0254     }
0255 
0256     if (m_active) {
0257         QPainterPath roundedRect;
0258         painter->setRenderHint(QPainter::Antialiasing);
0259         roundedRect.addRoundedRect(rect().adjusted(5, 5, -5, -5), 2, 2);
0260         painter->fillPath(roundedRect, QApplication::palette().text());
0261     }
0262 }
0263 
0264 void Monitor::Corner::hoverEnterEvent(QGraphicsSceneHoverEvent *e)
0265 {
0266     m_hover = true;
0267     update();
0268 }
0269 
0270 void Monitor::Corner::hoverLeaveEvent(QGraphicsSceneHoverEvent *e)
0271 {
0272     m_hover = false;
0273     update();
0274 }
0275 
0276 void Monitor::Corner::setActive(bool active)
0277 {
0278     m_active = active;
0279     update();
0280 }
0281 
0282 bool Monitor::Corner::active() const
0283 {
0284     return m_active;
0285 }
0286 } // namespace
0287 
0288 #include "moc_monitor.cpp"