BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
DocksController.cpp
Go to the documentation of this file.
1 // ************************************************************************************************
2 //
3 // BornAgain: simulate and fit reflection and scattering
4 //
5 //! @file GUI/coregui/Views/CommonWidgets/DocksController.cpp
6 //! @brief Implements class DocksController
7 //!
8 //! @homepage http://www.bornagainproject.org
9 //! @license GNU General Public License v3 or higher (see COPYING)
10 //! @copyright Forschungszentrum Jülich GmbH 2018
11 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS)
12 //
13 // ************************************************************************************************
14 
16 #include "Base/Utils/Assert.h"
18 #include <QAbstractItemView>
19 #include <QAction>
20 #include <QDockWidget>
21 #include <QEvent>
22 #include <QMainWindow>
23 #include <QMenu>
24 #include <QSettings>
25 #include <QTimer>
26 
27 namespace {
28 const char dockWidgetActiveState[] = "DockWidgetActiveState";
29 const char StateKey[] = "State";
30 const int settingsVersion = 2;
31 
32 QString stripAccelerator(const QString& text)
33 {
34  QString res = text;
35  for (int index = res.indexOf('&'); index != -1; index = res.indexOf('&', index + 1))
36  res.remove(index, 1);
37  return res;
38 }
39 
40 } // namespace
41 
42 DocksController::DocksController(QMainWindow* mainWindow)
43  : QObject(mainWindow), m_mainWindow(mainWindow)
44 {
45  m_mainWindow->setDocumentMode(true);
46  m_mainWindow->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::South);
47  m_mainWindow->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
48  m_mainWindow->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
49  m_mainWindow->installEventFilter(this);
50 }
51 
52 QDockWidget* DocksController::addDockForWidget(QWidget* widget)
53 {
54  auto dockWidget = new QDockWidget(m_mainWindow);
55  dockWidget->setWidget(widget);
56  dockWidget->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable
57  | QDockWidget::DockWidgetFloatable);
58  dockWidget->setObjectName(widget->objectName() + QLatin1String("DockWidget"));
59 
60  QString title = widget->windowTitle();
61  dockWidget->toggleViewAction()->setProperty("original_title", title);
62  title = stripAccelerator(title);
63  dockWidget->setWindowTitle(title);
64 
65  connect(dockWidget->toggleViewAction(), &QAction::triggered, [=]() {
66  if (dockWidget->isVisible())
67  dockWidget->raise();
68  });
69 
70  connect(dockWidget, &QDockWidget::visibilityChanged, [this, dockWidget](bool visible) {
72  dockWidget->setProperty(dockWidgetActiveState, visible);
73  });
74 
75  dockWidget->setProperty(dockWidgetActiveState, true);
76 
77  return dockWidget;
78 }
79 
80 void DocksController::addWidget(int id, QWidget* widget, Qt::DockWidgetArea area)
81 {
82  if (m_docks.find(id) != m_docks.end())
83  throw GUIHelpers::Error("DocksController::addWidget() -> Error. "
84  "Attempt to add widget id twice");
85 
86  auto dock = addDockForWidget(widget);
87  m_docks[id] = DockWidgetInfo(dock, widget, area);
88 
89  QList<QAbstractItemView*> frames = widget->findChildren<QAbstractItemView*>();
90  for (int i = 0; i < frames.count(); ++i)
91  frames[i]->setFrameStyle(QFrame::NoFrame);
92 }
93 
95 {
96  setTrackingEnabled(false);
97  for (auto dockWidget : dockWidgets()) {
98  dockWidget->setFloating(false);
99  m_mainWindow->removeDockWidget(dockWidget);
100  }
101 
102  for (auto& it : m_docks)
103  m_mainWindow->addDockWidget(it.second.area(), it.second.dock());
104 
105  // Fixes issue: https://bugreports.qt.io/browse/QTBUG-65592
106 #if QT_VERSION >= 0x050600
107  if (dockWidgets().size() > 0)
108  m_mainWindow->resizeDocks({dockWidgets().first()}, {10}, Qt::Horizontal);
109 #endif
110 
111  for (auto dockWidget : dockWidgets())
112  dockWidget->show();
113 
114  setTrackingEnabled(true);
115 }
116 
118 {
119  auto dock = findDock(id);
120  dock->setHidden(!dock->isHidden());
121 }
122 
123 QDockWidget* DocksController::findDock(int id)
124 {
125  ASSERT(m_docks.find(id) != m_docks.end());
126  return m_docks[id].dock();
127 }
128 
129 QDockWidget* DocksController::findDock(QWidget* widget)
130 {
131  for (auto& it : m_docks)
132  if (it.second.widget() == widget)
133  return it.second.dock();
134 
135  throw GUIHelpers::Error("DocksController::findDock() -> Can't find dock for widget");
136 }
137 
138 const QList<QDockWidget*> DocksController::dockWidgets() const
139 {
140  return m_mainWindow->findChildren<QDockWidget*>();
141 }
142 
143 //! Show docks with id's from the list. Other docks will be hidden.
144 
145 void DocksController::setVisibleDocks(const std::vector<int>& visibleDocks)
146 {
147  for (auto& it : m_docks) {
148  if (std::find(visibleDocks.begin(), visibleDocks.end(), it.first) != visibleDocks.end())
149  it.second.dock()->show();
150  else
151  it.second.dock()->hide();
152  }
153 }
154 
155 //! A hack to request update of QDockWidget size if its child (e.g. InfoWidget) wants it.
156 //! The problem bypassed here is that there is no direct method to QMainWindow to recalculate
157 //! position of splitters surrounding given QDockWidget. So our child QWidget requests here
158 //! the change of Min/Max size of QDockWidget, this will trigger recalculation of QDockWidget
159 //! layout and will force QDockWidget to respect sizeHints provided by ChildWidget. Later (in one
160 //! single timer shot) we return min/max sizes of QDockWidget back to re-enable splitters
161 //! functionality.
162 
164 {
165  QWidget* widget = qobject_cast<QWidget*>(sender());
166  ASSERT(widget);
167  QDockWidget* dock = findDock(widget);
168  ASSERT(dock);
169 
170  m_dock_info.m_dock = dock;
171  m_dock_info.m_min_size = dock->minimumSize();
172  m_dock_info.m_max_size = dock->maximumSize();
173 
174  if (height > 0) {
175  if (dock->height() < height)
176  dock->setMinimumHeight(height);
177  else
178  dock->setMaximumHeight(height);
179  }
180 
181  QTimer::singleShot(1, this, &DocksController::dockToMinMaxSizes);
182 }
183 
185 {
187  m_dock_info.m_dock->setMinimumSize(m_dock_info.m_min_size);
188  m_dock_info.m_dock->setMaximumSize(m_dock_info.m_max_size);
189  m_dock_info.m_dock = nullptr;
190 }
191 
193 {
194  if (enabled) {
196  for (auto dockWidget : dockWidgets())
197  dockWidget->setProperty(dockWidgetActiveState, dockWidget->isVisible());
198  } else {
200  }
201 }
202 
204 {
206  for (auto dockWidget : dockWidgets()) {
207  if (dockWidget->isFloating()) {
208  dockWidget->setVisible(visible && dockWidget->property(dockWidgetActiveState).toBool());
209  }
210  }
211  if (visible)
213 }
214 
215 bool DocksController::eventFilter(QObject* obj, QEvent* event)
216 {
217  if (event->type() == QEvent::Show)
219  else if (event->type() == QEvent::Hide)
221 
222  return QObject::eventFilter(obj, event);
223 }
224 
226 {
227  QList<QAction*> actions;
228  for (auto dockWidget : dockWidgets()) {
229  if (dockWidget->property("managed_dockwidget").isNull()
230  && dockWidget->parentWidget() == m_mainWindow) {
231  QAction* action = dockWidget->toggleViewAction();
232  action->setText(action->property("original_title").toString());
233  actions.append(action);
234  }
235  }
236  std::sort(actions.begin(), actions.end(), [](const QAction* action1, const QAction* action2) {
237  return stripAccelerator(action1->text()).toLower()
238  < stripAccelerator(action2->text()).toLower();
239  });
240 
241  foreach (QAction* action, actions)
242  menu->addAction(action);
243 }
244 
245 void DocksController::saveSettings(QSettings* settings) const
246 {
247  QHash<QString, QVariant> hash = saveSettings();
248  QHashIterator<QString, QVariant> it(hash);
249  while (it.hasNext()) {
250  it.next();
251  settings->setValue(it.key(), it.value());
252  }
253 }
254 
255 void DocksController::restoreSettings(const QSettings* settings)
256 {
257  QHash<QString, QVariant> hash;
258  foreach (const QString& key, settings->childKeys()) {
259  hash.insert(key, settings->value(key));
260  }
261  restoreSettings(hash);
262 }
263 
264 QHash<QString, QVariant> DocksController::saveSettings() const
265 {
266  QHash<QString, QVariant> settings;
267  settings.insert(QLatin1String(StateKey), m_mainWindow->saveState(settingsVersion));
268  for (auto dockWidget : dockWidgets()) {
269  settings.insert(dockWidget->objectName(), dockWidget->property(dockWidgetActiveState));
270  }
271  return settings;
272 }
273 
274 void DocksController::restoreSettings(const QHash<QString, QVariant>& settings)
275 {
276  QByteArray ba = settings.value(QLatin1String(StateKey), QByteArray()).toByteArray();
277  if (!ba.isEmpty())
278  m_mainWindow->restoreState(ba, settingsVersion);
279  for (auto widget : dockWidgets()) {
280  widget->setProperty(dockWidgetActiveState, settings.value(widget->objectName(), false));
281  }
282 }
Defines the macro ASSERT.
#define ASSERT(condition)
Definition: Assert.h:31
Defines class DocksController.
Defines class GUIHelpers functions.
Holds information about the widget and its dock.
bool m_handleDockVisibilityChanges
void restoreSettings(const QHash< QString, QVariant > &settings)
QDockWidget * addDockForWidget(QWidget *widget)
QHash< QString, QVariant > saveSettings() const
void setDockHeightForWidget(int height)
A hack to request update of QDockWidget size if its child (e.g.
void addDockActionsToMenu(QMenu *menu)
DockSizeInfo m_dock_info
void addWidget(int id, QWidget *widget, Qt::DockWidgetArea area)
virtual bool eventFilter(QObject *, QEvent *event)
QDockWidget * findDock(int id)
void handleWindowVisibilityChanged(bool visible)
void setVisibleDocks(const std::vector< int > &visibleDocks)
Show docks with id's from the list. Other docks will be hidden.
DocksController(QMainWindow *mainWindow)
QMainWindow * m_mainWindow
void setTrackingEnabled(bool enabled)
const QList< QDockWidget * > dockWidgets() const
void toggleDock(int id)
std::map< int, DockWidgetInfo > m_docks