File indexing completed on 2024-05-12 04:06:21
0001 /* 0002 SPDX-FileCopyrightText: 2009 Chani Armitage <chani@kde.org> 0003 SPDX-FileCopyrightText: 2010 Stefan Majewsky <majewsky@gmx.net> 0004 0005 SPDX-License-Identifier: LGPL-2.0-or-later 0006 */ 0007 0008 #include "mouseinputbutton.h" 0009 #include "mouseinputbutton_p.h" 0010 0011 #include <QApplication> 0012 #include <QHBoxLayout> 0013 #include <QStyleOptionButton> 0014 #include <QIcon> 0015 #include <KLocalizedString> 0016 0017 static QIcon clearIcon() 0018 { 0019 if (QApplication::isLeftToRight()) 0020 return QIcon::fromTheme( QStringLiteral( "edit-clear-locationbar-rtl" )); 0021 else 0022 return QIcon::fromTheme( QStringLiteral( "edit-clear-locationbar-ltr" )); 0023 } 0024 0025 Palapeli::MouseInputButton::MouseInputButton(QWidget* parent) 0026 : QPushButton(parent) 0027 , m_iconLabel(new QLabel) 0028 , m_mainLabel(new QLabel) 0029 , m_clearButton(new Palapeli::FlatButton(clearIcon())) 0030 , m_mouseAllowed(true) 0031 , m_wheelAllowed(true) 0032 , m_noButtonAllowed(true) 0033 , m_requiresValidation(false) 0034 { 0035 qRegisterMetaType<Palapeli::Trigger>(); 0036 connect(this, &QAbstractButton::clicked, this, &MouseInputButton::captureTrigger); 0037 connect(m_clearButton, &FlatButton::clicked, this, &MouseInputButton::clearTrigger); 0038 setCheckable(true); 0039 m_iconLabel->setPixmap(QIcon::fromTheme( QStringLiteral( "input-mouse" )).pixmap(22)); //TODO: respect global icon size configuration 0040 //setup child widgets 0041 m_iconLabel->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0042 m_mainLabel->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); 0043 m_mainLabel->setAlignment(Qt::AlignVCenter | Qt::AlignLeft); 0044 m_clearButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed); 0045 m_clearButton->setToolTip(i18n("Remove this trigger")); 0046 //setup layout 0047 QHBoxLayout* layout = new QHBoxLayout; 0048 setLayout(layout); 0049 layout->setContentsMargins(0, 0, 0, 0); 0050 layout->addWidget(m_iconLabel); 0051 layout->addWidget(m_mainLabel); 0052 layout->addWidget(m_clearButton); 0053 updateAppearance(); 0054 //calculate margin for layout from QStyle::sizeFromContents 0055 QStyleOptionButton opt; 0056 QPushButton::initStyleOption(&opt); 0057 const QSize bareSizeHint = layout->sizeHint(); 0058 const QSize fullSizeHint = style()->sizeFromContents(QStyle::CT_PushButton, &opt, bareSizeHint, this); 0059 const int marginX = (fullSizeHint.width() - bareSizeHint.width()) / 2; 0060 const int marginY = (fullSizeHint.height() - bareSizeHint.height()) / 2; 0061 layout->setContentsMargins(marginX, marginY, marginX, marginY); 0062 } 0063 0064 QSize Palapeli::MouseInputButton::sizeHint() const 0065 { 0066 //The layout's size hint is the right one, not the one calculated by QPushButton::sizeHint. 0067 return QWidget::sizeHint(); 0068 } 0069 0070 bool Palapeli::MouseInputButton::isMouseAllowed() const 0071 { 0072 return m_mouseAllowed; 0073 } 0074 0075 void Palapeli::MouseInputButton::setMouseAllowed(bool mouseAllowed) 0076 { 0077 m_mouseAllowed = mouseAllowed; 0078 } 0079 0080 bool Palapeli::MouseInputButton::isWheelAllowed() const 0081 { 0082 return m_wheelAllowed; 0083 } 0084 0085 void Palapeli::MouseInputButton::setWheelAllowed(bool wheelAllowed) 0086 { 0087 m_wheelAllowed = wheelAllowed; 0088 } 0089 0090 bool Palapeli::MouseInputButton::isNoButtonAllowed() const 0091 { 0092 return m_noButtonAllowed; 0093 } 0094 0095 void Palapeli::MouseInputButton::setNoButtonAllowed(bool noButtonAllowed) 0096 { 0097 m_noButtonAllowed = noButtonAllowed; 0098 } 0099 0100 bool Palapeli::MouseInputButton::requiresValidation() const 0101 { 0102 return m_requiresValidation; 0103 } 0104 0105 void Palapeli::MouseInputButton::setRequiresValidation(bool requiresValidation) 0106 { 0107 m_requiresValidation = requiresValidation; 0108 } 0109 0110 Palapeli::Trigger Palapeli::MouseInputButton::trigger() const 0111 { 0112 return m_trigger; 0113 } 0114 0115 void Palapeli::MouseInputButton::captureTrigger() 0116 { 0117 setChecked(true); 0118 m_clearButton->setVisible(false); //while capture is in progress 0119 m_mainLabel->setText(i18n("Input here...")); 0120 setToolTip(i18n("Hold down the modifier keys you want, then click a mouse button or scroll a mouse wheel here")); 0121 setFocus(Qt::MouseFocusReason); 0122 } 0123 0124 void Palapeli::MouseInputButton::clearTrigger() 0125 { 0126 setTrigger(Palapeli::Trigger()); 0127 } 0128 0129 bool Palapeli::MouseInputButton::event(QEvent* event) 0130 { 0131 0132 if (isChecked()) 0133 { 0134 //got a trigger or cancel 0135 switch ((int) event->type()) 0136 { 0137 case QEvent::Wheel: { 0138 if (!m_wheelAllowed) 0139 return false; 0140 const QWheelEvent* wEvent = static_cast<QWheelEvent*>(event); 0141 Palapeli::Trigger newTrigger; 0142 newTrigger.setModifiers(wEvent->modifiers()); 0143 const QPoint angleDelta = wEvent->angleDelta(); 0144 const Qt::Orientation orientation = (qAbs(angleDelta.x()) > qAbs(angleDelta.y()) ? Qt::Horizontal : Qt::Vertical); 0145 newTrigger.setWheelDirection(orientation); 0146 setTrigger(newTrigger); 0147 event->accept(); 0148 return true; 0149 } 0150 case QEvent::MouseButtonRelease: { 0151 if (!m_mouseAllowed) 0152 return false; 0153 const QMouseEvent* mEvent = static_cast<QMouseEvent*>(event); 0154 Palapeli::Trigger newTrigger; 0155 newTrigger.setModifiers(mEvent->modifiers()); 0156 newTrigger.setButton(mEvent->button()); 0157 setTrigger(newTrigger); 0158 event->accept(); 0159 return true; 0160 } 0161 case QEvent::MouseButtonPress: 0162 event->accept(); 0163 return true; 0164 case QEvent::KeyPress: { 0165 const QKeyEvent* kEvent = static_cast<QKeyEvent*>(event); 0166 if (kEvent->key() == Qt::Key_Escape) 0167 { 0168 //cancel 0169 setTrigger(m_trigger); 0170 event->accept(); 0171 return true; 0172 } 0173 if (kEvent->key() == Qt::Key_Space && m_noButtonAllowed) 0174 { 0175 //create trigger with NoButton (TODO: make this functionality more user-visible) 0176 Palapeli::Trigger newTrigger; 0177 newTrigger.setModifiers(kEvent->modifiers()); 0178 newTrigger.setButton(Qt::NoButton); 0179 setTrigger(newTrigger); 0180 event->accept(); 0181 return true; 0182 } 0183 [[fallthrough]]; 0184 } 0185 case QEvent::KeyRelease: { 0186 const QKeyEvent* kEvent = static_cast<QKeyEvent*>(event); 0187 showModifiers(kEvent->modifiers()); 0188 break; 0189 } 0190 } 0191 } 0192 bool ret = QPushButton::event(event); 0193 if (event->type() == QEvent::MouseButtonRelease) 0194 { 0195 const QMouseEvent* mEvent = static_cast<QMouseEvent*>(event); 0196 //fake a tooltip event 0197 //because otherwise they go away when you click and don't come back until you move the mouse 0198 QHelpEvent tip(QEvent::ToolTip, mEvent->pos(), mEvent->globalPosition().toPoint()); 0199 QApplication::sendEvent(this, &tip); 0200 } 0201 return ret; 0202 } 0203 0204 void Palapeli::MouseInputButton::setTrigger(const Palapeli::Trigger& trigger) 0205 { 0206 //NOTE: Invalid triggers need not be confirmed (esp. calls to clearTrigger()). 0207 if (m_requiresValidation && trigger.isValid() && m_trigger != trigger) 0208 { 0209 m_stagedTrigger = trigger; 0210 Q_EMIT triggerRequest(trigger); 0211 } 0212 else 0213 applyTrigger(trigger); 0214 } 0215 0216 void Palapeli::MouseInputButton::confirmTrigger(const Palapeli::Trigger& trigger) 0217 { 0218 if (m_stagedTrigger == trigger) 0219 applyTrigger(m_stagedTrigger); 0220 } 0221 0222 void Palapeli::MouseInputButton::updateAppearance() 0223 { 0224 //find caption 0225 static const QString noTriggerString = i18nc("This is used for describing that no mouse action has been assigned to this interaction plugin.", "None"); 0226 QString text = m_trigger.toString(); 0227 if (!m_trigger.isValid()) 0228 text = text.arg(noTriggerString); 0229 //apply properties 0230 setChecked(false); 0231 setToolTip(i18n("Click to change how an action is triggered")); 0232 m_mainLabel->setText(text); 0233 m_clearButton->setVisible(m_trigger.isValid()); 0234 } 0235 0236 void Palapeli::MouseInputButton::applyTrigger(const Palapeli::Trigger& trigger) 0237 { 0238 const bool announceChange = m_trigger != trigger; 0239 //apply new trigger 0240 m_trigger = trigger; 0241 m_stagedTrigger = Palapeli::Trigger(); 0242 updateAppearance(); 0243 //announce change 0244 if (announceChange) 0245 Q_EMIT triggerChanged(trigger); 0246 } 0247 0248 void Palapeli::MouseInputButton::showModifiers(Qt::KeyboardModifiers modifiers) 0249 { 0250 Palapeli::Trigger dummyTrigger; 0251 dummyTrigger.setModifiers(modifiers); 0252 m_mainLabel->setText(dummyTrigger.toString().arg(i18n("Input here..."))); 0253 } 0254 0255 0256 // 0257 0258 #include "moc_mouseinputbutton.cpp" 0259 #include "moc_mouseinputbutton_p.cpp"