Dragging QDockWidgets between QMainWindows - c++

I have a question regarding dragging QDockWidgets between QMainWindows. I have an application with different "DockAreas" which have a QMainWindow member with one or more QDockWidgets. Additionally, I want to make QDockWidgets drag-and-droppable into other, already floating QDockWidgets (not tabbed!).
To have the nice hover-effect upon drag-and-drop, the floating QDockWidgets are embedded in a new instance of my DockArea with the QMainWindow member. Now I intercept the mousemove event of the dragged QDockWidget and change its parent when it hovers over another QMainWindow of my DockArea. Unfortunately, this causes a crash and it looks like the original QLayoutItem, where this QDockWidget was in, is gone.
void QDockWidgetPrivate::moveEvent(QMoveEvent *event)
{
// ...
// now this widgetItem member of state is kaputt
layout->hover(state->widgetItem, globalMousePos);
}
I found different suggestions for dragging-dropping QDockWidgets between QMainWindows on the internet:
1) Re-parenting - that's what I am trying atm
2) QDrag - did not work for me, async exec() breaks my application and at the end I have to reparent anyway
3) Call addDockWidget(...) of the new QMainWindow - docks the QDockWidget and I would have to make it floating again and somehow "continue the dragging"
I am open to any new suggestions :) Thank you in advance!

I'm wrestling with the same Problem right now and want to share.
Ich found this forum post dealing with the same problem: http://www.qtcentre.org/threads/41847-Dragging-QDockWidgets-between-QMainWindows
This solution is however flawed: it doesn't work properly if you allow the Dockables to be floating. And the enterEvent does not actually trigger when you enter the other window and still dragging the QDockWidget. It only triggers after release.
I'm also going for the reparenting (1). QDrag seems problematic when you also want to allow rearranging the QDockWidget within the Window.
I think one would correctly do this by reacting to the drop/release of the dragged QDockWidget. But I can't find an event or signal for this.

Related

Hide QSplitter child but keep the position of the handle

I have a QSplitter and two child widgets in it. Let's say p_widget_1 and p_widget_2. p_widget_1 is allowed to be closed, it has a Close button and when Close is clicked I call p_widget_1->hide(). QSplitter doc says:
When you hide() a child, its space will be distributed among the other children. It will be reinstated when you show() it again.
But I want the QSplitter's handle to keep its position as I have a requirement that p_widget_2 should have fixed size.
How do I achieve it? My current approach is calling p_splitter->setSizes() when necessary but it doesn't always work in all my cases.
I am on Windows, QT is 5.4.1, Visual Studio 2013.
I didn't try on a particular QWidget (like your QSplitter) but this is what I do when I want to restore the geometry of a window after it is closed:
Before the child is hidden, save the QByteArray retrieved with the
following function (on your QSplitter)
QByteArray QWidget::saveGeometry() const
After the child is displayed again, call the following method with
the QByteArray you just stored
bool QWidget::restoreGeometry(const QByteArray & geometry)
Documentation here.
I am not sure this solve your problem or not. Try 'saveSate' method to save state of QSplitter and restore it by 'restoreState' method.

C++ Qt Inherit QMessageBox to delay user input in order to prevent unintended action

Problem
Windows has a system setting that will cause the mouse pointer to jump (move) to a new focus element automatically, e.g. the default button of a dialog that pops up. While the advantage is an increase in speed and a reduction of mouse movements, it has a disadvantage:
If this happens just when before the user clicks on another element, the user is unable to abort his/her action in time and will immediately accept the dialogs default button because the focus is moved by the system. Usually this may entail cumbersome work to retrace the steps up to this point (think a file chooser dialog that forgot the very long path you input previously) but it could also mean triggering an irreversible process (e.g. file deletion).
Aim
Essentially I would like to disable the dialog's inputs for a small amount of time, just enough to prevent an inadvertant mouse click or keyboard button press.
Question
It comes down to a C++ question, namely how to access the base classes' objects (GUI widgets) from the inheriting class, i.e.
disable the button widgets of a QMessageBox
start a single shot QTimer and connect it to a slot that
enables the previously disabled widgets
(As alternative, I probably could reimplement input event handlers that suppress all input for a specific amount of time, but although I intend to keep that time very short (e.g. 100 ms), the user is not informed of the disabled input using that method.)
A simple class derived from QDialogBox can be found at http://www.qtforum.org/article/24342/messagebox-auto-close-mouse-event-close.html.
Do you need to use one of the "native"-ish message boxes provided by the QMessageBox static functions?
Otherwise, that's pretty simple to achieve, by building a QMessageBox and adding standard buttons to it:
QMessageBox *messageBox = new QMessageBox;
QPushButton *okButton = messageBox->addButton(QMessageBox::Ok);
okButton->setEnabled(false);
// use a QTimer to add logic to reenable the button
// use QCursor to move the mouse cursor on the button
// add a nice countdown in the button's label, like Firefox does
// add other nice UX touches as wanted
Last points are left as an exercise to the reader :)
To en/disable the buttons in QMessagebox one would need access to them.
qmessagebox.cpp uses buttonBox = new QDialogButtonBox; and the addButton() method
d->buttonBox->addButton(button, (QDialogButtonBox::ButtonRole)role);
d->customButtonList.append(button);
But I don't understand Qt internals and am unable to find these in qmessagebox.h and thus fail to find out if there is a chance to access the buttons..

QMainWindow no longer active window

I'm making a simple game with Qt and I'd like to pause the game when the user switches to a different window (this could be by minimizing it or by accidentally clicking on a window beside it, etc). My game is wrapped in a QMainWindow, so I'd like to be able to detect when that loses focus.
I've tried a few different methods for this, but I haven't been successful. I first tried overloading QMainWindow's focusOutEvent, but this method was only called when I first gave the window focus with setFocus. I also tried to overload the window's event(QEvent *) method to check for QEvent::ApplicationActive and QEvent::ApplicationDeactivate.
I would post the code for my QMainWindow but there isn't much to show, I literally just tried to implement those two methods but neither were called. I did nothing else to set up those methods (maybe I'm missing a step?).
Does anyone know a good way to determine if your QMainWindow has "lost focus"?
I had a similar need once, and resolved it by overloading event(QEvent*) method of my QMainWindow :
bool MyMainWindow::event(QEvent * e)
{
switch(e->type())
{
// ...
case QEvent::WindowActivate :
// gained focus
break ;
case QEvent::WindowDeactivate :
// lost focus
break ;
// ...
} ;
return QMainWindow::event(e) ;
}
From the documentation -
A widget normally must setFocusPolicy() to something other than
Qt::NoFocus in order to receive focus events. (Note that the
application programmer can call setFocus() on any widget, even those
that do not normally accept focus.)
From this part of the documentation, about focusPolicy-
This property holds the way the widget accepts keyboard focus.
The policy is Qt::TabFocus if the widget accepts keyboard focus by
tabbing, Qt::ClickFocus if the widget accepts focus by clicking,
Qt::StrongFocus if it accepts both, and Qt::NoFocus (the default) if
it does not accept focus at all.
You must enable keyboard focus for a widget if it processes keyboard
events. This is normally done from the widget's constructor. For
instance, the QLineEdit constructor calls
setFocusPolicy(Qt::StrongFocus).
So set your focus policies accordingly, I guess then you will receive appropriate focus events.
Try QWidget::isActiveWindow() .

QDockWidget is closed if main window is minimized

I'm using Qt 4.7 on Windows 7 Ultimate 32 bit.
The QMainWindow of my program has a QDockWidget. I've noticed that if I minimize the main window by the minimize button on the title bar, after restoring it the dock widget is closed. I didn't write any support for a feature like this!
How does this happen and how to prevent this?
Thanks.
I encountered this error when writing my own application. I have QDockWidget with options for my application. Using Qt Creator I created menu with QAction actionMenu which was checkable. Then I connected QDockWidget and QAction like this:
QObject::connect(ui->dockWidget, SIGNAL(visibilityChanged(bool)),
ui->actionMenu, SLOT(setChecked(bool)));
QObject::connect(ui->actionMenu, SIGNAL(toggled(bool)),
ui->dockWidget, SLOT(setVisible(bool)));
The order of connection is not important. And then when I minimized application with QDockWidget being visible, after I restored it QDockWidget was closed and actionMenu was unchecked.
In fact there are two solutions. First which works for me is to use SIGNAL(triggered(bool)) instead of SIGNAL(toggled(bool)):
QObject::connect(ui->dockWidget, SIGNAL(visibilityChanged(bool)),
ui->actionMenu, SLOT(setChecked(bool)));
QObject::connect(ui->actionMenu, SIGNAL(triggered(bool)),
ui->dockWidget, SLOT(setVisible(bool)));
The second solution uses action which you can obtain from QDockWidget:
// Retrieve action from QDockWidget.
QAction *action = ui->dockWidget->toggleViewAction();
// Adjust any parameter you want.
action->setText(QString("&Menu"));
action->setShortcut(QKeySequence(Qt::CTRL + Qt::Key_M));
action->setStatusTip(QString("Press to show/hide menu widget."));
action->setChecked(true);
// Install action in the menu.
ui->menuOptions->addAction(action);
I know for sure that SIGNAL(toggled(bool)) was causing in my application closure of QDockWidget.
I faced the same problem... I managed to get rid of it by using a method called StoreWindowsLayout and RestoreWindowsLayout.
StoreWindowsLayout will save the content of the ByteArray returned by the Method QMainwindow::saveState().
RestoreWindowsLayout will restore that bytearray, and therefore your windows layout, the qdockwidget visibility state and so on...
I call StoreWindowsLayout on ApplicationMainFrm::changeEvent, on ApplicationMainFrm::closeEvent (it's likely this one you'll need) and in ApplicationMainFrm::hide().
Then I use restoreWindowsLayout in ApplicationMainFrm::showEvent.
Exemple of use of restoreWindowsLayout in my MainForm :
void ApplicationMainFrm::showEvent(QShowEvent* pEvent)
{
QMainWindow::showEvent(pEvent);
restoreWindowsLayout();
}
Hope it helps !

Qt -how to know whether content in child widgets has been changed?

In QMainWindow I have 2 QSplitters. In that splitters I have QTextEdit, QLineEdits, QTableWinget, Ragio buttons and so on... I want to know if somthing has been chaged after pressing File->New menu button. Is there any general method for doing this?
Somwhere I have read that it is recomended to use isWindowModified() function of QMainWindow, but seems it doesn't work.
setWindowModified() does not propagate the windowModified flag to the parents. This bug is described here: https://bugreports.qt.io/browse/QTBUG-20150. I have just tried it and indeed it did not work.
The isWindowModified() could be useful here since according to http://doc.trolltech.com/4.6/qwidget.html#windowModified-prop it propagates up to the parent.
However, I think you would need to set this yourself. For example, if you clicked the new button which leads to some text being inserted into a QTextEdit, you still need to call QTextEdit's setWindowModified() function - which will then propagate up to your QMainWindow - and you can just check QMainWindow afterwards. (However, you wouldn't know which children were modified)
Maybe you should have a look at QWidget::changeEvent.