Воспроизведение поведения минимизации ОС в пользовательской строке заголовка, выполненное в QML

#qt #qml

Вопрос:

Мне поручено создать настраиваемую строку заголовка для нашего приложения. Помимо прочего, она должна иметь закругленные углы и кнопку настроек. Он будет работать исключительно в Windows.

Наше приложение использует Qt и QML для интерфейса. Итак, единственный способ, которым я мог найти, как это сделать, — сделать окно приложения без рамки и создать строку заголовка с нуля.

Это мой тестовый код:

 import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0

ApplicationWindow {

    id: mainWindow
    visible: true
    visibility: Window.Maximized
    title: qsTr("Hello World")
    flags: Qt.FramelessWindowHint | Qt.Window | Qt.WA_TranslucentBackground
    //flags: Qt.Window | Qt.WA_TranslucentBackground
    color: "#00000000"

    TitleBar {
        id: mainTitleBar
        width: mainWindow.width;
        height: mainWindow.height*0.018
        color: "#aaaaaa"
        onCloseApplication: {
            Qt.quit();
        }
        onMinimizeApplication: {
            mainWindow.visibility = Window.Minimized
        }
    }

    Component.onCompleted: {
        console.log("Size: "   mainWindow.width   "x"   mainWindow.height)
        mainTitleBar.width = mainWindow.width
        mainTitleBar.height = mainWindow.height*0.023;
    }

    Rectangle {
        id: content
        width: mainWindow.width
        height: mainWindow.height - mainTitleBar.height
        anchors.top: mainTitleBar.bottom
        anchors.left: mainTitleBar.left
        color: "#00ff00"
    }

}
 

И

Вот код строки заголовка (TitleBar.js файл):

 import QtQuick 2.0
import QtQuick.Controls 2.0

Rectangle {
    /*
     * Requires setting up of
     * -> width
     * -> height
     * -> title text
     * -> icon path.
     * -> Background color.
     */

    id: vmWindowTitleBar
    border.width: 0
    x: 0
    y: 0
    radius: 20

    signal closeApplication();
    signal minimizeApplication();

    // The purpose of this rectangle is to erase the bottom rounded corners
    Rectangle {
        width: parent.width
        height: parent.height/2;
        anchors.bottom: parent.bottom
        anchors.left: parent.left
        border.width: 0
        color: parent.color
    }

    Text {
        id: titleBarText
        text: "This is The Title Bar"
        anchors.verticalCenter: parent.verticalCenter
        anchors.leftMargin: parent.width*0.018
    }

    Button {
        id: minimizeButton
        width: height
        height: vmWindowTitleBar.height*0.8
        anchors.right: closeButton.right
        anchors.verticalCenter: parent.verticalCenter
        anchors.rightMargin: parent.width*0.018

        background: Rectangle {
            id: btnMinimizeRect
            color: vmWindowTitleBar.color
            anchors.fill: parent
        }

        onPressed:{
            minimizeApplication()
        }

        scale: pressed? 0.8:1;

        contentItem: Canvas {
            id: btnMinimizeCanvas
            contextType: "2d"
            anchors.fill: parent
            onPaint: {
                var ctx = btnMinimizeCanvas.getContext("2d");
                var h = minimizeButton.height;
                var w = minimizeButton.width;
                ctx.reset();
                ctx.strokeStyle = minimizeButton.pressed? "#58595b": "#757575";
                ctx.lineWidth = 6;
                ctx.lineCap = "round"
                ctx.moveTo(0,h);
                ctx.lineTo(w,h);
                ctx.closePath();
                ctx.stroke();
            }
        }

    }

    Button {
        id: closeButton
        //hoverEnabled: false
        width: height
        height: vmWindowTitleBar.height*0.8
        anchors.right: parent.right
        anchors.verticalCenter: parent.verticalCenter
        anchors.rightMargin: parent.width*0.018
        background: Rectangle {
            id: btnCloseRect
            color: vmWindowTitleBar.color
            anchors.fill: parent
        }

        onPressed:{
            closeApplication()
        }

        scale: pressed? 0.8:1;

        Behavior on scale{
            NumberAnimation {
                duration: 10
            }
        }

        contentItem: Canvas {
            id: btnCloseCanvas
            contextType: "2d"
            anchors.fill: parent
            onPaint: {
                var ctx = btnCloseCanvas.getContext("2d");
                var h = closeButton.height;
                var w = closeButton.width;
                ctx.reset();
                ctx.strokeStyle = closeButton.pressed? "#58595b": "#757575";
                ctx.lineWidth = 2;
                ctx.lineCap = "round"
                ctx.moveTo(0,0);
                ctx.lineTo(w,h);
                ctx.moveTo(w,0);
                ctx.lineTo(0,h);
                ctx.closePath();
                ctx.stroke();
            }
        }
    }

}
 

Теперь проблема связана с минимизацией приложения. Первое, что я понимаю, это то, что при использовании флага Qt.FramelessWindowHint значок не отображается на панели задач Windows. Кроме того, если я минимизирую это, это произойдет:

Минимизировать поведение

И если я нажму на нее, она не восстановится.

Итак, мой вопрос в том, есть ли способ воспроизвести обычное поведение минимизации при нажатии кнопки минимизации?

Или, в качестве альтернативы, есть ли способ полностью настроить строку заголовка приложения, чтобы я мог добиться внешнего вида, установленного нашим дизайнером пользовательского интерфейса?

ПРИМЕЧАНИЕ: текущий вид — это просто быстрый тест. Я не установил градиент, шрифт или вышеупомянутую кнопку настроек.

Ответ №1:

Что касается меня, игра с безрамочными окнами и прозрачным фоном — это своего рода обходной путь. Насколько я знаю, единственный способ применить пользовательскую форму к окну — это QWindow::setMask. Sinse Window является производным от QWindow вы можете сделать это таким образом.

Например, в main.cpp:

 QWindow *wnd = qobject_cast<QWindow *>(engine.rootObjects().at(0));
auto f = [wnd]() {
    QPainterPath path;
    path.addRoundedRect(QRectF(0, 0, wnd->geometry().width(), wnd->geometry().height()), 30, 30);
    QRegion region(path.toFillPolygon().toPolygon());
    wnd->setMask(region);
};
QObject::connect(wnd, amp;QWindow::widthChanged, f);
QObject::connect(wnd, amp;QWindow::heightChanged, f);
f();
 

Поскольку вы «вырезаете» фигуру из самого окна, исключая строку заголовка и фреймы, вы можете оставить флаги окна как есть.

Комментарии:

1. Я попробую это и посмотрю, что у меня получится. Это подход к взаимному тестированию. Но как бы вы, например, добавили кнопку к этому примеру? Мне нужно добавить кнопку «Настройки» со значком шестеренки, кроме нее.

Ответ №2:

Посмотрите на этот способ, я пытаюсь создать что-то, что вы делаете, но полностью меняете свой код.

проблема, которая приводит к изменению размера вашего окна после сворачивания окна, заключается в том, что вы не установили начальное width значение и height для окна, поэтому при сворачивании приложения оно отображается с минимальной шириной и высотой.

итак, вам нужно добавить только это main.qml и установить начальную ширину и высоту на максимум.

     width: maximumWidth
    height:maximumHeight
 

но в приведенном ниже коде я также меняю кое-что еще.

Например, вам не нужно было излучать сигналы, а затем перехватывать их в main.qml, к которому у вас есть доступ mainWindow TitleBar.qml .

в TitleBar.qml :

 import QtQuick 2.0
import QtQuick.Controls 2.0

Rectangle {
    anchors.fill: parent
    height: 30

    Row {
        id: row
        anchors.fill: parent

        Label {
            id: label
            text: qsTr("Title ")

        }

        Button {
            id: button
            x: parent.width -80
            text: qsTr("close")

            onClicked:
            {
                mainWindow.close()

            }
        }

        Button {
            id: button1
            x: parent.width -160
            width: 90
            text: qsTr("Minimized")

            onClicked:
            {
                mainWindow.showMinimized()
            }
        }
    }


}
 

и в main.qml :

 import QtQuick 2.12
import QtQuick.Window 2.12
import QtQuick.Controls 2.0

import "."

Window {

    id: mainWindow
    visible: true

    visibility: Window.FullScreen
    title: qsTr("Hello World")
    flags: Qt.FramelessWindowHint | Qt.Window | Qt.WA_TranslucentBackground

    width: maximumWidth
    height:maximumHeight

    Rectangle {
        id: content
        anchors.fill: parent
        x: 0
        y: 20
        width: mainWindow.width
        height: mainWindow.height - mainTitleBar.height
        anchors.top: mainTitleBar.bottom
        anchors.left: mainTitleBar.left
        color: "#00ff00"



    }
    TitleBar {
        id: mainTitleBar
        color: "#aaaaaa"
        anchors.bottomMargin: parent.height -40
        anchors.fill: parent



    }

}
 

Комментарии:

1. Привет. Вы пробовали свой код? Из-за добавления строк, которые вы предложили в мой код (в самом начале), мое приложение вообще не отображается. Кроме того, проблема по-прежнему остается, на панели задач нет значка, и при сворачивании появляется точно такое же изображение. Изменение, которое я сделал, — это использование метода showMinimize .

2. @aarelovich. Я не могу запустить ваш код, потому что я изменяю его и добавляю свой код для ответа.

3. ОК. Таким образом, в принципе, пока установлен флаг Qt.FramelessWindowHint, ОС (Windows) в основном не будет создавать значок для приложения на панели задач, а обычного поведения минимизации просто нет.