Table of contents
I tend to make the applications I develop to be as customizable as possible because I personally enjoy customizable software. Not everyone has the same workflow.
But If you decide to accommodate a large number of workflows, you run into the risk of making your application too complicated, messy and crammed.
I encounter this problem especially when making a toolbar. Adding widgets such as combo/spin boxes would cost a lot of space, and tool buttons do not offer a lot of interaction, which made me think of using the latter as an anchor instead. A way to display and hide a larger set of settings and widgets.
Take a look:
Basic template
A floating widget is not contained in any layout, it…well…floats, meaning its position is relative to the screen, not the application’s window.
To achieve that, we could use Qt::popup
window flag:
setWindowFlag(Qt::Popup,true);
This would take care of making the widget a top-level one, modal and hide when clicked outside of it. Now remains controlling where it appears.
Let’s use a QToolButton
in QToolBar
, and make the widget popup where its center would align with the tool button’s center.
We could implement that by calculating its (x,y) coordinates in showEvent
:
Calculating x:
Get the x coordinate of the tool button’s center.
Subtract half of the widget width from it.
int parentGlobalX = parentWidget()->mapToGlobal( parentWidget()->rect().center() ).x();
//push it to the left by half of its width
int x = parentGlobalX - width() / 2;
Calculating y:
Use the tool button’s height as the y coordinate of a QPoint
, so it can be mapped to global.
int y = parentWidget()->mapToGlobal( QPoint(0, parentWidget()->geometry().height() ) ).y();
Setting new Position :
Use QWidget::move, and pass it the new (x,y) coordinates:
move(x,y);
Here’s the resulting showEvent
:
void showEvent(QShowEvent *event)
{
int parentGlobalX = parentWidget()->mapToGlobal( parentWidget()->rect().center() ).x();
//push it to the left by half of its width
int x = parentGlobalX - width() / 2;
int y = parentWidget()->mapToGlobal( QPoint(0, parentWidget()->geometry().height() ) ).y();
move(x,y);
}
Here’s how it looks:
Usage
Full Class:
class FloatWidget : public QWidget
{
public:
FloatWidget(QWidget *parent) : QWidget(parent)
{
setWindowFlag(Qt::Popup,true);
setStyleSheet("background: white;");
}
protected:
void showEvent(QShowEvent *event)
{
int parentGlobalX = parentWidget()->mapToGlobal( parentWidget()->rect().center() ).x();
//push it to the left by half of its width
int x = parentGlobalX - width() / 2;
int y = parentWidget()->mapToGlobal( QPoint(0, parentWidget()->geometry().height() ) ).y();
move(x,y);
}
};
Here’s an example of how to use it in a QMainWindow
:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
floating_widgetA = new FloatWidget(ui->toolBar->widgetForAction(ui->actionA));
}
//use QAction triggered signal and connect it to a slot
//to call the floating widget's show in
void MainWindow::on_actionA_triggered()
{
floating_widgetA->show();
}
From this, you can customize it however you like, I made mine animated and wrote a custom paintEvent
for it; I’ve also added a pointer widget to help identify which widget it belongs to.
You can take a look at my public repository on Bitbucket to get some ideas.