Saturday, February 26, 2022

[SOLVED] QDialog::move() not considering taskbar on Ubuntu with multiple screens

Issue

Normally, moving a QDialog using QDialog::move() positions the dialog outside of taskbars. However, on Ubuntu 20.04 with two monitors it is not the case with frameless Dialogs :

href="https://i.stack.imgur.com/Eu6bW.png" rel="nofollow noreferrer">bug_frameless

This does not happen if the dialog is not frameless :

enter image description here

This behaviour has been observed on Ubuntu 20.04. It also happens only under some configurations :

  • Main monitor needs to be on the right side, with task bar on the left (between the two monitors)
  • Left monitor needs to have a lower resolution than the right one
  • Fractional scaling needs to be disabled

Here is the code for a minimally reproducible example used in the screenshots:

#ifndef BUGDIALOG_H
#define BUGDIALOG_H

#include <QDialog>

namespace Ui {
class BugDialog;
}

class BugDialog : public QDialog
{
    Q_OBJECT

public:
    explicit BugDialog(QWidget *parent = nullptr);
    ~BugDialog();

private slots:
    void on_moveButton_clicked();

private:
    Ui::BugDialog *ui;
};

#endif // BUGDIALOG_H



#include "bugdialog.h"
#include "ui_bugdialog.h"

BugDialog::BugDialog(QWidget *parent) :
    QDialog(parent),
    ui(new Ui::BugDialog)
{
    ui->setupUi(this);
    ui->xPosEdit->setText("3200");
    ui->yPosEdit->setText("1000");
}

BugDialog::~BugDialog()
{
    delete ui;
}

void BugDialog::on_moveButton_clicked()
{
    int x = ui->xPosEdit->text().toInt();
    int y = ui->yPosEdit->text().toInt();
    if (x > -1 && x > -1)
        move(x, y);
}

The main window is less interesting, it only creates the child window controlling its WindowFlags property :

#ifndef MAINWINDOW_H
#define MAINWINDOW_H

#include <QMainWindow>
#include "bugdialog.h"

QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACE

class MainWindow : public QMainWindow
{
    Q_OBJECT

public:
    MainWindow(QWidget *parent = nullptr);
    ~MainWindow();

private slots:
    void on_pushButton_clicked();

    void on_framelessBox_stateChanged(int arg1);

private:
    void hideDialog();

    Ui::MainWindow *ui;
    BugDialog* dialog;
};
#endif // MAINWINDOW_H



#include "mainwindow.h"
#include "ui_mainwindow.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    dialog = new BugDialog(nullptr);
    dialog->hide();
}

MainWindow::~MainWindow()
{
    delete ui;
    delete dialog;
}

void MainWindow::on_pushButton_clicked()
{
    if (dialog->isHidden())
    {
        dialog->show();
        ui->pushButton->setText("Hide dialog");
    }
    else
    {
        hideDialog();
    }
}

void MainWindow::on_framelessBox_stateChanged(int)
{
    auto windowType = ui->framelessBox->isChecked() ? Qt::FramelessWindowHint : Qt::Dialog;
    dialog->setWindowFlags(windowType);
    hideDialog();
}

void MainWindow::hideDialog()
{
    dialog->hide();
    ui->pushButton->setText("Show dialog");
}

This looks like a bug in Qt. Does anyone know if it is expected behaviour? Or how to get around this?


Solution

I didn't find a proper solution or satisfying workaround for this issue, but found a partial solution that is half satisfying :

  1. Before each move() on the dialog, set its flag to Qt::Window (no frameless) and hide it.
  2. Override the moveEvent() handler, set the window flag to Qt::FramelessWindowHint and show it.

Here are the two changes I made on this example :

void BugDialog::on_moveButton_clicked()
{
    int x = ui->xPosEdit->text().toInt();
    int y = ui->yPosEdit->text().toInt();
    if (x > -1 && x > -1)
    {
        hide();
        setWindowFlags(Qt::Window);
        move(x, y);
    }
}

void BugDialog::moveEvent(QMoveEvent *)
{
    QTimer::singleShot(500, this, [this](){
        this->setWindowFlags(Qt::FramelessWindowHint);
        this->show();
    });
}

I also tried changing the dialog painting. The idea was to set window flags as a "framefull" dialog but paint the dialog as if it had the FramelessWindowHint flag. I found no acceptable/affordable solution with this idea.



Answered By - Mickaël C. Guimarães
Answer Checked By - Katrina (WPSolving Volunteer)