BornAgain  1.19.79
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/View/Common/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/Util/Assert.h"
17 #include "GUI/Util/Error.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 
29 const char dockWidgetActiveState[] = "DockWidgetActiveState";
30 const char StateKey[] = "State";
31 const int settingsVersion = 2;
32 
33 QString stripAccelerator(const QString& text)
34 {
35  QString res = text;
36  for (int index = res.indexOf('&'); index != -1; index = res.indexOf('&', index + 1))
37  res.remove(index, 1);
38  return res;
39 }
40 
41 } // namespace
42 
44  : QObject(mainWindow)
45  , m_mainWindow(mainWindow)
46 {
47  m_mainWindow->setDocumentMode(true);
48  m_mainWindow->setTabPosition(Qt::AllDockWidgetAreas, QTabWidget::South);
49  m_mainWindow->setCorner(Qt::BottomLeftCorner, Qt::LeftDockWidgetArea);
50  m_mainWindow->setCorner(Qt::BottomRightCorner, Qt::RightDockWidgetArea);
51  m_mainWindow->installEventFilter(this);
52 }
53 
54 QDockWidget* DocksController::addDockForWidget(QWidget* widget)
55 {
56  auto* dockWidget = new QDockWidget(m_mainWindow);
57  dockWidget->setWidget(widget);
58  dockWidget->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetClosable
59  | QDockWidget::DockWidgetFloatable);
60  dockWidget->setObjectName(widget->objectName() + QLatin1String("DockWidget"));
61 
62  QString title = widget->windowTitle();
63  dockWidget->toggleViewAction()->setProperty("original_title", title);
64  title = stripAccelerator(title);
65  dockWidget->setWindowTitle(title);
66 
67  connect(dockWidget->toggleViewAction(), &QAction::triggered, [=]() {
68  if (dockWidget->isVisible())
69  dockWidget->raise();
70  });
71 
72  connect(dockWidget, &QDockWidget::visibilityChanged, [this, dockWidget](bool visible) {
74  dockWidget->setProperty(dockWidgetActiveState, visible);
75  });
76 
77  dockWidget->setProperty(dockWidgetActiveState, true);
78 
79  return dockWidget;
80 }
81 
82 void DocksController::addWidget(int id, QWidget* widget, Qt::DockWidgetArea area)
83 {
84  if (m_docks.find(id) != m_docks.end())
85  throw Error("DocksController::addWidget() -> Error. "
86  "Attempt to add widget id twice");
87 
88  auto* dock = addDockForWidget(widget);
89  m_docks[id] = DockWidgetInfo(dock, widget, area);
90 
91  QList<QAbstractItemView*> frames = widget->findChildren<QAbstractItemView*>();
92  for (int i = 0; i < frames.count(); ++i)
93  frames[i]->setFrameStyle(QFrame::NoFrame);
94 }
95 
97 {
98  setTrackingEnabled(false);
99  for (auto* dockWidget : dockWidgets()) {
100  dockWidget->setFloating(false);
101  m_mainWindow->removeDockWidget(dockWidget);
102  }
103 
104  for (auto& it : m_docks)
105  m_mainWindow->addDockWidget(it.second.area(), it.second.dock());
106 
107  // Fixes issue: https://bugreports.qt.io/browse/QTBUG-65592
108 #if QT_VERSION >= 0x050600
109  if (!dockWidgets().empty())
110  m_mainWindow->resizeDocks({dockWidgets().first()}, {10}, Qt::Horizontal);
111 #endif
112 
113  for (auto* dockWidget : dockWidgets())
114  dockWidget->show();
115 
116  setTrackingEnabled(true);
117 }
118 
120 {
121  if (auto* dock = findDock(id))
122  dock->setHidden(!dock->isHidden());
123 }
124 
125 void DocksController::setDockVisible(int id, bool visible)
126 {
127  if (auto* dock = findDock(id))
128  dock->setHidden(!visible);
129 }
130 
131 QDockWidget* DocksController::findDock(int id)
132 {
133  if (m_docks.find(id) == m_docks.end())
134  return nullptr;
135 
136  return m_docks[id].dock();
137 }
138 
139 QDockWidget* DocksController::findDock(QWidget* widget)
140 {
141  for (auto& it : m_docks)
142  if (it.second.widget() == widget)
143  return it.second.dock();
144 
145  return nullptr;
146 }
147 
148 QList<QDockWidget*> DocksController::dockWidgets() const
149 {
150  return m_mainWindow->findChildren<QDockWidget*>();
151 }
152 
153 //! Show docks with id's from the list. Other docks will be hidden.
154 
155 void DocksController::setVisibleDocks(const std::vector<int>& visibleDocks)
156 {
157  for (auto& it : m_docks) {
158  if (std::find(visibleDocks.begin(), visibleDocks.end(), it.first) != visibleDocks.end())
159  it.second.dock()->show();
160  else
161  it.second.dock()->hide();
162  }
163 }
164 
165 //! A hack to request update of QDockWidget size if its child (e.g. InfoWidget) wants it.
166 //! The problem bypassed here is that there is no direct method to QMainWindow to recalculate
167 //! position of splitters surrounding given QDockWidget. So our child QWidget requests here
168 //! the change of Min/Max size of QDockWidget, this will trigger recalculation of QDockWidget
169 //! layout and will force QDockWidget to respect sizeHints provided by ChildWidget. Later (in one
170 //! single timer shot) we return min/max sizes of QDockWidget back to re-enable splitters
171 //! functionality.
172 
174 {
175  QWidget* widget = qobject_cast<QWidget*>(sender());
176  ASSERT(widget);
177  QDockWidget* dock = findDock(widget);
178  ASSERT(dock);
179 
180  m_dock_info.m_dock = dock;
181  m_dock_info.m_min_size = dock->minimumSize();
182  m_dock_info.m_max_size = dock->maximumSize();
183 
184  if (height > 0) {
185  if (dock->height() < height)
186  dock->setMinimumHeight(height);
187  else
188  dock->setMaximumHeight(height);
189  }
190 
191  QTimer::singleShot(1, this, &DocksController::dockToMinMaxSizes);
192 }
193 
195 {
196  ASSERT(m_dock_info.m_dock);
197  m_dock_info.m_dock->setMinimumSize(m_dock_info.m_min_size);
198  m_dock_info.m_dock->setMaximumSize(m_dock_info.m_max_size);
199  m_dock_info.m_dock = nullptr;
200 }
201 
203 {
204  if (enabled) {
206  for (auto* dockWidget : dockWidgets())
207  dockWidget->setProperty(dockWidgetActiveState, dockWidget->isVisible());
208  } else {
210  }
211 }
212 
214 {
216  for (auto* dockWidget : dockWidgets()) {
217  if (dockWidget->isFloating())
218  dockWidget->setVisible(visible && dockWidget->property(dockWidgetActiveState).toBool());
219  }
220  if (visible)
222 }
223 
224 bool DocksController::eventFilter(QObject* obj, QEvent* event)
225 {
226  if (event->type() == QEvent::Show)
228  else if (event->type() == QEvent::Hide)
230 
231  return QObject::eventFilter(obj, event);
232 }
233 
235 {
236  QList<QAction*> actions;
237  for (auto* dockWidget : dockWidgets()) {
238  if (dockWidget->parentWidget() == m_mainWindow) {
239  QAction* action = dockWidget->toggleViewAction();
240  action->setText(action->property("original_title").toString());
241  actions.append(action);
242  }
243  }
244  std::sort(actions.begin(), actions.end(), [](const QAction* action1, const QAction* action2) {
245  return stripAccelerator(action1->text()).toLower()
246  < stripAccelerator(action2->text()).toLower();
247  });
248 
249  foreach (QAction* action, actions)
250  menu->addAction(action);
251 }
252 
253 void DocksController::saveSettings(QSettings* settings) const
254 {
255  QHash<QString, QVariant> hash = saveSettings();
256  QHashIterator<QString, QVariant> it(hash);
257  while (it.hasNext()) {
258  it.next();
259  settings->setValue(it.key(), it.value());
260  }
261 }
262 
263 void DocksController::restoreSettings(const QSettings* settings)
264 {
265  QHash<QString, QVariant> hash;
266  foreach (const QString& key, settings->childKeys()) {
267  hash.insert(key, settings->value(key));
268  }
269  restoreSettings(hash);
270 }
271 
272 QHash<QString, QVariant> DocksController::saveSettings() const
273 {
274  QHash<QString, QVariant> settings;
275  settings.insert(QLatin1String(StateKey), m_mainWindow->saveState(settingsVersion));
276  for (auto* dockWidget : dockWidgets())
277  settings.insert(dockWidget->objectName(), dockWidget->property(dockWidgetActiveState));
278  return settings;
279 }
280 
281 void DocksController::restoreSettings(const QHash<QString, QVariant>& settings)
282 {
283  QByteArray ba = settings.value(QLatin1String(StateKey), QByteArray()).toByteArray();
284  if (!ba.isEmpty())
285  m_mainWindow->restoreState(ba, settingsVersion);
286  for (auto* widget : dockWidgets())
287  widget->setProperty(dockWidgetActiveState, settings.value(widget->objectName(), false));
288 }
Defines class DocksController.
Defines error class.
Holds information about the widget and its dock.
void setDockVisible(int id, bool visible=true)
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. InfoWidget) wants it....
void addDockActionsToMenu(QMenu *menu)
DockSizeInfo m_dock_info
void addWidget(int id, QWidget *widget, Qt::DockWidgetArea area)
QDockWidget * findDock(int id)
bool eventFilter(QObject *, QEvent *event) override
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)
QList< QDockWidget * > dockWidgets() const
void toggleDock(int id)
std::map< int, DockWidgetInfo > m_docks
static QMainWindow * mainWindow
Definition: Globals.h:22