Как прикрепить QDialog к границам экрана, как это делает Skype?

Давным-давно я пытался найти способ, как приклеить окно QDialog к границам экрана для моих небольших проектов, как это делают окна Skype, но мне это не удалось. Может быть, я искал этот код не в том месте, поэтому теперь я ищу решение здесь, в стеке! :)

Итак, кто-нибудь имеет дело с каким-то таким кодом, ссылками, образцами?

На мой взгляд, мы должны переопределить функцию QDialog moveEvent, как показано ниже, но этот код не работает:

void    CDialog::moveEvent(QMoveEvent * event) {

    QRect wndRect;
    int leftTaskbar = 0, rightTaskbar = 0, topTaskbar = 0, bottomTaskbar = 0;
//  int top = 0, left = 0, right = 0, bottom = 0;

    wndRect = this->frameGeometry();

    // Screen resolution
    int screenWidth =   QApplication::desktop()->width();
    int screenHeight =  QApplication::desktop()->height();

    int wndWidth = wndRect.right() - wndRect.left();
    int wndHeight = wndRect.bottom() - wndRect.top();

    int posX = event->pos().x();
    int posY = event->pos().y();

    // Snap to screen border
    // Left border
    if (posX >= -m_nXOffset + leftTaskbar &&
        posX <= leftTaskbar + m_nXOffset) {
        //left = leftTaskbar;
        this->move(leftTaskbar, posY);
        return;
    }

    // Top border
    if (posY >= -m_nYOffset &&
        posY <= topTaskbar + m_nYOffset) {
        //top = topTaskbar;
        this->move(posX, topTaskbar);
        return;
    }

    // Right border
    if (posX + wndWidth <= screenWidth - rightTaskbar + m_nXOffset &&
        posX + wndWidth >= screenWidth - rightTaskbar - m_nXOffset) {
        //right = screenWidth - rightTaskbar - wndWidth;
        this->move(screenWidth - rightTaskbar - wndWidth, posY);
        return;
    }

    // Bottom border
    if (posY + wndHeight <= screenHeight - bottomTaskbar + m_nYOffset &&
        posY + wndHeight >= screenHeight - bottomTaskbar - m_nYOffset) {
        //bottom = screenHeight - bottomTaskbar - wndHeight;
        this->move(posX, screenHeight - bottomTaskbar - wndHeight);
        return;
    }

    QDialog::moveEvent(event);
}

Спасибо.


person mosg    schedule 26.04.2010    source источник


Ответы (2)


Как вы думали, вы можете добиться этого в функции moveEvent. Я предполагаю, что следующий код делает свое дело, но поскольку мне нечего здесь тестировать, я напишу псевдокод:

Сначала получите доступную область экрана:

const QRect screen = QApplication::availableGeometry(this);
// This get the screen rect where you can drag a dialog

Затем получите положение вашего диалога относительно рабочего стола (если ваш диалог является дочерним элементом другого виджета, вам необходимо преобразовать координаты из виджета относительно рабочего стола):

const QRect dialog = geometry();
// Do here transformation

Теперь проверьте, находится ли диалоговое окно рядом с границей экрана.

if( abs(dialog.left()-screen.left() < OFFSET )
    move(screen.left(), dialog.top();
else if( abs(dialog.top()-screen.top() < OFFSET )
    move(dialog.left(), screen.top() )
// etc. for the 2 other cases

Дайте мне знать, если это работает

person Patrice Bernassola    schedule 26.04.2010
comment
@Patrice Bernassola: Спасибо за ответ. Я думаю, что мне нужно обновить свой образец, потому что он более готов к тестированию/редактированию... Поскольку я давно пользуюсь linux, в KDE есть глобальное решение для этого случая: все окна/диалоги могут быть заклеены к границам окон, так что, может быть, стоит поискать в исходниках KDE... - person mosg; 27.04.2010

В описании свойства pos из документации QWidget есть следующее предупреждение о перемещении окна внутри метода обработки события перемещения.

Внимание! Вызов move() или setGeometry() внутри moveEvent() может привести к бесконечной рекурсии.

Тем не менее, нет правильного способа вставить диалоговое окно внутри границы экрана.

Примечание. Поведение, которое вы наблюдали в KDE, связано с оконным менеджером. На самом деле именно диспетчер окон упорядочивает окна приложений (например, диалоговые окна) так, чтобы они отображались на экране. Диспетчер окон KDE имеет возможность сделать так, чтобы все окна приложений (называемые клиентом) придерживались границы.

person Lohrun    schedule 11.05.2010
comment
Спасибо за ответ. Я попробую ваше решение завтра, но в исходном сообщении был код... - person mosg; 11.05.2010
comment
Код, который на самом деле не работает... Мне удалось восстановить положение виджета, чтобы он мог прилипать к границе экрана. Но в некоторых крайних случаях обработка событий начинала зацикливаться на неопределенный срок. Я могу восстановить код, но вы должны сейчас, потому что в некоторых случаях он не будет работать. - person Lohrun; 11.05.2010
comment
Да, я проверил, и он отлично работает только с верхней границей, а не с остальными. Грустно, но это значит, что мы на правильном пути решения этой проблемы... - person mosg; 12.05.2010