ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvModalShortcut.cpp
Go to the documentation of this file.
1 // ----------------------------------------------------------------------------
2 // - CloudViewer: www.cloudViewer.org -
3 // ----------------------------------------------------------------------------
4 // Copyright (c) 2018-2024 www.cloudViewer.org
5 // SPDX-License-Identifier: MIT
6 // ----------------------------------------------------------------------------
7 
9 
10 // app
11 #include <CVLog.h>
12 
13 #include <QAction>
14 #include <QShortcut>
15 #include <QWidget>
16 
17 //-----------------------------------------------------------------------------
18 ecvModalShortcut::ecvModalShortcut(const QKeySequence& key,
19  QAction* action,
20  QWidget* parent)
21  : Superclass(parent), m_key(key), m_action(action) {
22  // Create the underlying QShortcut
23  // Note: Default context is WindowShortcut, but we'll set it properly
24  // in setContextWidget() if needed
25  m_shortcut = new QShortcut(key, parent);
26 
27  // Connect shortcut activation to our signal
28  QObject::connect(m_shortcut, &QShortcut::activated, this,
30 
31  // If an action is provided, trigger it when activated
32  if (m_action) {
33  QObject::connect(m_shortcut, &QShortcut::activated, m_action,
34  &QAction::trigger);
35  }
36 }
37 
38 //-----------------------------------------------------------------------------
40  Q_EMIT unregister();
41  delete m_shortcut;
42 }
43 
44 //-----------------------------------------------------------------------------
45 void ecvModalShortcut::setContextWidget(QWidget* contextWidget,
46  Qt::ShortcutContext contextArea) {
47  bool enabled = this->isEnabled();
48 
49  if (m_shortcut) {
50  // Check if we're already using this widget and context
51  if (qobject_cast<QWidget*>(m_shortcut->parent()) == contextWidget) {
52  // Same parent - update context if needed
53  // CRITICAL: QShortcut's context can be changed after creation,
54  // but for ApplicationShortcut (especially for system shortcuts like
55  // "Ctrl+C"), it's safer to recreate to ensure proper registration
56  if (m_shortcut->context() != contextArea) {
57  // For ApplicationShortcut, recreate to ensure proper
58  // registration This is especially important for "Ctrl+C" which
59  // may conflict with system shortcuts
60  if (contextArea == Qt::ApplicationShortcut) {
61  delete m_shortcut;
62  m_shortcut = new QShortcut(m_key, contextWidget);
63  m_shortcut->setEnabled(enabled);
64  m_shortcut->setContext(contextArea);
65 
66  // Reconnect signals
67  QObject::connect(m_shortcut, &QShortcut::activated, this,
69  if (m_action) {
70  QObject::connect(m_shortcut, &QShortcut::activated,
71  m_action, &QAction::trigger);
72  }
73  } else {
74  // For other contexts, just update
75  m_shortcut->setContext(contextArea);
76  }
77  }
78  return;
79  }
80  }
81 
82  // To change parents, it's best to start over
83  delete m_shortcut;
84 
85  if (!contextWidget && contextArea != Qt::ApplicationShortcut) {
86  // We need to keep a shortcut around, but don't pay attention
87  // to it since the context widget is null.
88  m_shortcut = nullptr;
89  } else {
90  // Create new shortcut with the new parent
91  m_shortcut = new QShortcut(m_key, contextWidget);
92  m_shortcut->setEnabled(enabled);
93  m_shortcut->setContext(contextArea);
94 
95  // Reconnect signals
96  QObject::connect(m_shortcut, &QShortcut::activated, this,
98  if (m_action) {
99  QObject::connect(m_shortcut, &QShortcut::activated, m_action,
100  &QAction::trigger);
101  }
102  }
103 }
104 
105 //-----------------------------------------------------------------------------
107  return m_shortcut ? m_shortcut->isEnabled() : false;
108 }
109 
110 //-----------------------------------------------------------------------------
111 void ecvModalShortcut::setEnabled(bool enable, bool changeFocus) {
112  if (!m_shortcut) {
113  // Shortcut was destroyed (e.g., parent widget deleted)
114  return;
115  }
116 
117  if (enable) {
118  if (!m_shortcut->isEnabled()) {
119  // Emit signal first (so ecvKeySequences can disable siblings)
120  Q_EMIT enabled();
121  m_shortcut->setEnabled(true);
122 
123  // If requested, give focus to the context widget
124  // so users can immediately use the shortcut
125  auto ctxt = m_shortcut->context();
126  if ((ctxt == Qt::WidgetShortcut ||
127  ctxt == Qt::WidgetWithChildrenShortcut) &&
128  changeFocus) {
129  auto* parent = dynamic_cast<QWidget*>(m_shortcut->parent());
130  if (parent) {
131  parent->setFocus(Qt::OtherFocusReason);
132  }
133  }
134  }
135  } else {
136  if (m_shortcut->isEnabled()) {
137  m_shortcut->setEnabled(false);
138  Q_EMIT disabled();
139  }
140  }
141 }
142 
143 //-----------------------------------------------------------------------------
144 QKeySequence ecvModalShortcut::keySequence() const { return m_key; }
void setEnabled(bool shouldEnable, bool changeFocus=true)
ecvModalShortcut(const QKeySequence &key, QAction *action=nullptr, QWidget *parent=nullptr)
QPointer< QAction > m_action
~ecvModalShortcut() override
QPointer< QShortcut > m_shortcut
QKeySequence m_key
bool isEnabled() const
void setContextWidget(QWidget *contextWidget, Qt::ShortcutContext contextArea=Qt::WindowShortcut)
QKeySequence keySequence() const