ACloudViewer  3.9.4
A Modern Library for 3D Data Processing
ecvShortcutDecorator.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 
12 
13 // CV_CORE_LIB
14 #include <CVLog.h>
15 
16 #include <QEvent>
17 #include <QFrame>
18 #include <QKeySequence>
19 
20 //-----------------------------------------------------------------------------
22  : Superclass(parent),
23  m_pressed(false),
24  m_silent(false),
25  m_allowRefocus(false) {
26  if (!parent) {
27  CVLog::Error("[ecvShortcutDecorator] Cannot decorate null widget!");
28  return;
29  }
30 
31  // Set up the frame for decoration
32  parent->setLineWidth(2);
33  parent->setFrameStyle(QFrame::NoFrame | QFrame::Plain);
34  this->markFrame(false, QColor(0, 0, 0, 0));
35 
36  // Install event filter to monitor mouse events
37  parent->installEventFilter(this);
38 }
39 
40 //-----------------------------------------------------------------------------
42  if (!shortcut) {
44  "[ecvShortcutDecorator] Attempted to add null shortcut!");
45  return;
46  }
47 
48  m_shortcuts.push_back(shortcut);
49 
50  // Make border active; color it; enable all shortcuts for this widget
51  this->onShortcutEnabled();
52 
53  // Connect signals
54  QObject::connect(shortcut, &ecvModalShortcut::enabled, this,
56  QObject::connect(shortcut, &ecvModalShortcut::disabled, this,
58 
59  CVLog::Print(QString("[ecvShortcutDecorator] Added shortcut: %1")
60  .arg(shortcut->keySequence().toString()));
61 }
62 
63 //-----------------------------------------------------------------------------
65  // All attached shortcuts should have the same state, so just take the first
66  // (if any).
67  if (m_shortcuts.empty()) {
68  return false;
69  }
70 
71  for (auto& shortcut : m_shortcuts) {
72  if (shortcut) {
73  return shortcut->isEnabled();
74  }
75  }
76 
77  return false;
78 }
79 
80 //-----------------------------------------------------------------------------
82  if (m_silent) {
83  return;
84  }
85 
86  // One shortcut was just enabled... but to mark ourselves
87  // as active, we must activate all of them.
88  m_silent = true;
89  for (auto& shortcut : m_shortcuts) {
90  if (shortcut) {
91  shortcut->setEnabled(true, m_allowRefocus);
92  }
93  }
94  m_silent = false;
95  m_allowRefocus = false; // Always reset after use.
96 
97  // Set the visual style (use link color from palette)
98  auto* frame = this->decoratedFrame();
99  if (frame) {
100  this->markFrame(true, frame->palette().link().color());
101  }
102 }
103 
104 //-----------------------------------------------------------------------------
106  if (m_silent) {
107  return;
108  }
109 
110  // One shortcut was just disabled... but to mark ourselves
111  // as inactive, we must deactivate all of them.
112  m_silent = true;
113  for (auto& shortcut : m_shortcuts) {
114  if (shortcut) {
115  shortcut->setEnabled(false);
116  }
117  }
118  m_silent = false;
119 
120  // Set the visual style (transparent)
121  this->markFrame(false, QColor(0, 0, 0, 0));
122 }
123 
124 //-----------------------------------------------------------------------------
125 void ecvShortcutDecorator::setEnabled(bool enable, bool refocusWhenEnabling) {
126  if (enable) {
127  m_allowRefocus = refocusWhenEnabling; // This will be reset inside
128  // onShortcutEnabled.
129  // This has the effect of turning all shortcuts on.
130  this->onShortcutEnabled();
131  } else {
132  // This has the effect of turning all shortcuts off.
133  this->onShortcutDisabled();
134  }
135 }
136 
137 //-----------------------------------------------------------------------------
139  return qobject_cast<QFrame*>(this->parent());
140 }
141 
142 //-----------------------------------------------------------------------------
143 bool ecvShortcutDecorator::eventFilter(QObject* obj, QEvent* event) {
144  if (obj == this->parent()) {
145  auto* frame = this->decoratedFrame();
146  if (!frame) {
147  return Superclass::eventFilter(obj, event);
148  }
149 
150  switch (event->type()) {
151  case QEvent::Enter:
152  // On mouse enter, make the border darker
153  this->markFrame(true, frame->palette().link().color().darker());
154  break;
155 
156  case QEvent::Leave: {
157  // On mouse leave, restore the border to active or transparent
158  bool ena = this->isEnabled();
159  this->markFrame(ena, ena ? frame->palette().link().color()
160  : QColor(0, 0, 0, 0));
161  m_pressed = false;
162  } break;
163 
164  case QEvent::MouseButtonPress:
165  m_pressed = true;
166  break;
167 
168  case QEvent::MouseButtonRelease:
169  if (m_pressed) {
170  m_pressed = false;
171 
172  // Reorder how shortcuts will cycle so that the previous
173  // sibling is this shortcut's "next".
174  for (auto& shortcut : m_shortcuts) {
175  if (shortcut) {
177  }
178  }
179 
180  // Toggle the enabled state
181  this->setEnabled(!this->isEnabled(), true);
182 
183  // Eat this mouse event:
184  return true;
185  }
186  break;
187 
188  default:
189  // do nothing
190  break;
191  }
192  }
193  return Superclass::eventFilter(obj, event);
194 }
195 
196 //-----------------------------------------------------------------------------
197 void ecvShortcutDecorator::markFrame(bool active, const QColor& frameColor) {
198  (void)active; // This can be used to modulate line width, frame shape.
199 
200  auto* frame = this->decoratedFrame();
201  if (!frame) {
202  return;
203  }
204 
205  frame->setFrameShape(QFrame::Box);
206  frame->setLineWidth(2);
207 
208  // Use stylesheet to set the border color
209  QString objectName = frame->objectName();
210  if (objectName.isEmpty()) {
211  // If no object name, set a temporary one
212  objectName = QString("ecvDecoratedFrame_%1")
213  .arg(reinterpret_cast<quintptr>(frame));
214  frame->setObjectName(objectName);
215  }
216 
217  frame->setStyleSheet(
218  QString("QFrame#%1 { border: 2px solid rgba(%2, %3, %4, %5); }")
219  .arg(objectName)
220  .arg(frameColor.red())
221  .arg(frameColor.green())
222  .arg(frameColor.blue())
223  .arg(frameColor.alpha()));
224 }
Rect frame
MouseEvent event
static bool Warning(const char *format,...)
Prints out a formatted warning message in console.
Definition: CVLog.cpp:133
static bool Print(const char *format,...)
Prints out a formatted message in console.
Definition: CVLog.cpp:113
static bool Error(const char *format,...)
Display an error dialog with formatted message.
Definition: CVLog.cpp:143
void reorder(ecvModalShortcut *target)
static ecvKeySequences & instance()
Manage an action and/or widget's responsivity to a shortcut.
QKeySequence keySequence() const
bool eventFilter(QObject *obj, QEvent *event) override
ecvShortcutDecorator(QFrame *parent)
bool m_silent
Prevent recursive signaling inside onShortcutEnabled/onShortcutDisabled.
QList< QPointer< ecvModalShortcut > > m_shortcuts
QFrame * decoratedFrame() const
virtual void setEnabled(bool enable, bool refocusWhenEnabling=false)
void markFrame(bool active, const QColor &frameColor)
void addShortcut(ecvModalShortcut *shortcut)
virtual void onShortcutEnabled()