Sine Striker
2023-12-14 e18723d66cf470720c29a9d2848150ea1cbe5450
Add example resources
10个文件已修改
13个文件已添加
635 ■■■■■ 已修改文件
examples/CMakeLists.txt 4 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/CMakeLists.txt 7 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/dark-style.qss 153 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/main.cpp 10 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/mainwindow.cpp 170 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/mainwindow.h 6 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/mainwindow/mainwindow.qrc 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/qml/CMakeLists.txt 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/window-bar.qrc 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/window-bar/close.svg 15 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/window-bar/fullscreen.svg 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/window-bar/icon.svg 18 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/window-bar/maximize.svg 12 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/window-bar/minimize.svg 11 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/window-bar/more-line.svg 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/resources/window-bar/restore.svg 16 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/widgetframe/CMakeLists.txt 5 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/widgetframe/windowbutton.cpp 92 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/widgetframe/windowbutton.h 48 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/shared/widgetframe/windowbutton_p.h 27 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/abstractwindowcontext.cpp 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/core/contexts/win32windowcontext.cpp 1 ●●●● 补丁 | 查看 | 原始文档 | blame | 历史
src/widgets/widgetwindowagent_win.cpp 2 ●●● 补丁 | 查看 | 原始文档 | blame | 历史
examples/CMakeLists.txt
@@ -13,6 +13,10 @@
add_subdirectory(shared)
if(QWINDOWKIT_BUILD_WIDGETS)
add_subdirectory(mainwindow)
endif()
if(QWINDOWKIT_BUILD_QUICK)
add_subdirectory(qml)
endif()
examples/mainwindow/CMakeLists.txt
@@ -3,7 +3,12 @@
file(GLOB _src *.h *.cpp)
qwk_add_example(${PROJECT_NAME}
    SOURCES ${_src}
    SOURCES ${_src} mainwindow.qrc ../shared/resources/window-bar.qrc
    QT_LINKS Core Gui Widgets
    LINKS QWKWidgets WidgetFrame
)
set_target_properties(${PROJECT_NAME} PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED TRUE
)
examples/mainwindow/dark-style.qss
New file
@@ -0,0 +1,153 @@
/* Window bar */
QWK--WindowBar[bar-active=true] {
    background-color: #3C3C3C;
}
QWK--WindowBar[bar-active=false] {
    background-color: #505050;
}
/* Title label */
QWK--WindowBar>QLabel#win-title-label {
    padding: 0;
    border: none;
    color: #ECECEC;
    background-color: transparent;
}
/* System buttons */
QWK--WindowBar>QAbstractButton[system-button=true] {
    qproperty-iconSize: 12px 12px;
    min-width: 50px;
    border: none;
    padding: 0;
    background-color: transparent;
}
QWK--WindowBar>QAbstractButton#min-button {
    qproperty-iconNormal: url(":/window-bar/minimize.svg");
    qproperty-iconSize: 12px 12px;
}
QWK--WindowBar>QAbstractButton#min-button:hover,
QWK--WindowBar>QAbstractButton#min-button:pressed {
    background-color: rgba(0, 0, 0, 15%);
}
QWK--WindowBar>QAbstractButton#max-button {
    qproperty-iconNormal: url(":/window-bar/maximize.svg");
    qproperty-iconChecked: url(":/window-bar/restore.svg");
}
QWK--WindowBar>QAbstractButton#max-button:hover,
QWK--WindowBar>QAbstractButton#max-button:pressed {
    background-color: rgba(0, 0, 0, 15%);
}
QWK--WindowBar>QAbstractButton#close-button {
    qproperty-iconNormal: url(":/window-bar/close.svg");
}
QWK--WindowBar>QAbstractButton#close-button:hover,
QWK--WindowBar>QAbstractButton#close-button:pressed {
    background-color: #e81123;
}
/* Icon button */
QWK--WindowBar>QAbstractButton#icon-button {
    qproperty-iconNormal: url(":/window-bar/icon.svg");
    qproperty-iconSize: 20px 20px;
    min-width: 40px;
    border: none;
    padding: 0;
    background-color: transparent;
}
/* Menu Bar */
QMenuBar {
    background-color: transparent;
    border: none;
}
QMenuBar>QToolButton#qt_menubar_ext_button {
    qproperty-icon: url(":/window-bar/more-line.svg");
}
QMenuBar>QToolButton#qt_menubar_ext_button:hover,
QMenuBar>QToolButton#qt_menubar_ext_button:pressed {
    background-color: rgba(255, 255, 255, 10%);
}
QMenuBar::item {
    color: #CCCCCC;
    border: none;
    padding: 8px 12px;
}
QMenuBar::item:selected {
    background-color: rgba(255, 255, 255, 10%);
}
/* Menu */
QMenu {
    padding: 4px;
    background: #303030;
    border: 1.25px solid transparent;
}
QMenu::indicator {
    left: 6px;
    width: 20px;
    height: 20px;
}
QMenu::icon {
    left: 6px;
}
QMenu::item {
    background: transparent;
    color: #CCCCCC;
    padding: 6px 24px;
}
QMenu::item:selected {
    color: white;
    background-color: #0060C0;
}
QMenu::item:disabled {
    color: #666666;
    background-color: transparent;
}
QMenu::separator {
    height: 2px;
    background-color: #5B5B5B;
    margin: 6px 0;
}
/* Window */
MainWindow {
    background-color: #1E1E1E;
}
QWidget#clock-widget {
    font-size: 75px;
    color: #FEFEFE;
    font-weight: bold;
    background-color: transparent;
}
examples/mainwindow/main.cpp
@@ -1,4 +1,5 @@
#include <QtWidgets/QApplication>
#include <QtGui/QFont>
#include <QtCore/QDebug>
#include "mainwindow.h"
@@ -17,6 +18,15 @@
    QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
        Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
    QApplication a(argc, argv);
#ifdef Q_OS_WINDOWS
    a.setFont([]() {
        QFont f("Microsoft YaHei");
        f.setPixelSize(15);
        return f;
    }());
#endif
    MainWindow w;
    w.show();
    return a.exec();
examples/mainwindow/mainwindow.cpp
@@ -1,17 +1,23 @@
#include "mainwindow.h"
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtWidgets/QApplication>
#include <QtWidgets/QStyle>
#include <QtWidgets/QPushButton>
#include <QWKWidgets/widgetwindowagent.h>
#include <widgetframe/windowbar.h>
#include <widgetframe/windowbutton.h>
class ClockWidget : public QPushButton {
class ClockWidget : public QLabel {
public:
    explicit ClockWidget(QWidget *parent = nullptr) : QPushButton(parent) {
    explicit ClockWidget(QWidget *parent = nullptr) : QLabel(parent) {
        startTimer(100);
        setAlignment(Qt::AlignCenter);
    }
    ~ClockWidget() override = default;
@@ -24,9 +30,79 @@
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
    installWindowAgent();
    auto clockWidget = new ClockWidget();
    clockWidget->setObjectName("clock-widget");
    clockWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    setCentralWidget(clockWidget);
    if (QFile qss(":/dark-style.qss"); qss.open(QIODevice::ReadOnly | QIODevice::Text)) {
        setStyleSheet(QString::fromUtf8(qss.readAll()));
    }
    setWindowTitle("Example MainWindow");
    resize(640, 480);
}
static inline void emulateLeaveEvent(QWidget *widget) {
    Q_ASSERT(widget);
    if (!widget) {
        return;
    }
    QTimer::singleShot(0, widget, [widget]() {
#if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
        const QScreen *screen = widget->screen();
#else
        const QScreen *screen = widget->windowHandle()->screen();
#endif
        const QPoint globalPos = QCursor::pos(screen);
        if (!QRect(widget->mapToGlobal(QPoint{0, 0}), widget->size()).contains(globalPos)) {
            QCoreApplication::postEvent(widget, new QEvent(QEvent::Leave));
            if (widget->testAttribute(Qt::WA_Hover)) {
                const QPoint localPos = widget->mapFromGlobal(globalPos);
                const QPoint scenePos = widget->window()->mapFromGlobal(globalPos);
                static constexpr const auto oldPos = QPoint{};
                const Qt::KeyboardModifiers modifiers = QGuiApplication::keyboardModifiers();
#if (QT_VERSION >= QT_VERSION_CHECK(6, 4, 0))
                const auto event =
                    new QHoverEvent(QEvent::HoverLeave, scenePos, globalPos, oldPos, modifiers);
                Q_UNUSED(localPos);
#elif (QT_VERSION >= QT_VERSION_CHECK(6, 3, 0))
                const auto event =  new QHoverEvent(QEvent::HoverLeave, localPos, globalPos, oldPos, modifiers);
                Q_UNUSED(scenePos);
#else
                const auto event =  new QHoverEvent(QEvent::HoverLeave, localPos, oldPos, modifiers);
                Q_UNUSED(scenePos);
#endif
                QCoreApplication::postEvent(widget, event);
            }
        }
    });
}
MainWindow::~MainWindow() {
}
bool MainWindow::event(QEvent *event) {
    switch (event->type()) {
        case QEvent::WindowActivate: {
            auto menu = menuWidget();
            menu->setProperty("bar-active", true);
            style()->polish(menu);
            break;
        }
        case QEvent::WindowDeactivate: {
            auto menu = menuWidget();
            menu->setProperty("bar-active", false);
            style()->polish(menu);
            break;
        }
        default:
            break;
    }
    return QMainWindow::event(event);
}
void MainWindow::installWindowAgent() {
@@ -35,37 +111,45 @@
        qFatal("Frameless handle failed to initialize.");
    }
    auto titleLabel = new QLabel();
    titleLabel->setAlignment(Qt::AlignCenter);
    auto menuBar = []() {
        auto menuBar = new QMenuBar();
        auto file = new QMenu("File(&F)");
        file->addAction(new QAction("New(&N)"));
        file->addAction(new QAction("Open(&O)"));
        auto file = new QMenu("File(&F)", menuBar);
        file->addAction(new QAction("New(&N)", menuBar));
        file->addAction(new QAction("Open(&O)", menuBar));
        auto edit = new QMenu("Edit(&E)");
        edit->addAction(new QAction("Undo(&U)"));
        edit->addAction(new QAction("Redo(&R)"));
        auto edit = new QMenu("Edit(&E)", menuBar);
        edit->addAction(new QAction("Undo(&U)", menuBar));
        edit->addAction(new QAction("Redo(&R)", menuBar));
        menuBar->addMenu(file);
        menuBar->addMenu(edit);
        return menuBar;
    }();
    menuBar->setObjectName("win-menu-bar");
    auto iconButton = new QPushButton("I");
    iconButton->setAttribute(Qt::WA_Hover);
    iconButton->setMouseTracking(true);
    auto minButton = new QPushButton("—");
    minButton->setAttribute(Qt::WA_Hover);
    minButton->setMouseTracking(true);
    auto maxButton = new QPushButton("🗖");
    auto titleLabel = new QLabel();
    titleLabel->setAlignment(Qt::AlignCenter);
    titleLabel->setObjectName("win-title-label");
    auto iconButton = new QWK::WindowButton();
    iconButton->setObjectName("icon-button");
    iconButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    auto minButton = new QWK::WindowButton();
    minButton->setObjectName("min-button");
    minButton->setProperty("system-button", true);
    minButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    auto maxButton = new QWK::WindowButton();
    maxButton->setCheckable(true);
    maxButton->setAttribute(Qt::WA_Hover);
    maxButton->setMouseTracking(true);
    auto closeButton = new QPushButton("✖");
    closeButton->setAttribute(Qt::WA_Hover);
    closeButton->setMouseTracking(true);
    maxButton->setObjectName("max-button");
    maxButton->setProperty("system-button", true);
    maxButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    auto closeButton = new QWK::WindowButton();
    closeButton->setObjectName("close-button");
    closeButton->setProperty("system-button", true);
    closeButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
    auto windowBar = new QWK::WindowBar();
    windowBar->setIconButton(iconButton);
@@ -83,29 +167,37 @@
    agent->setSystemButton(QWK::WindowAgentBase::Close, closeButton);
    agent->setHitTestVisible(menuBar, true);
#ifdef Q_OS_WINDOWS
    // Emulate Window system menu button behaviors
    connect(iconButton, &QAbstractButton::clicked, agent, [this, iconButton, agent] {
        setProperty("double-click-close", false);
        // Pick a suitable time threshold
        QTimer::singleShot(75, this, [this, iconButton, agent]() {
            if (property("double-click-close").toBool())
                return;
            agent->showSystemMenu(iconButton->mapToGlobal({0, iconButton->height()}));
        });
    });
    connect(iconButton, &QWK::WindowButton::doubleClicked, this, [this]() {
        setProperty("double-click-close", true);
        close();
    });
#endif
    connect(windowBar, &QWK::WindowBar::minimizeRequested, this, &QWidget::showMinimized);
    connect(windowBar, &QWK::WindowBar::maximizeRequested, this, [this](bool max) {
    connect(windowBar, &QWK::WindowBar::maximizeRequested, this, [this, maxButton](bool max) {
        if (max) {
            showMaximized();
        } else {
            showNormal();
        }
        // It's a Qt issue that if a QAbstractButton::clicked triggers a window's maximization,
        // the button remains to be hovered until the mouse move. As a result, we need to
        // manully send leave events to the button.
        emulateLeaveEvent(maxButton);
    });
    connect(windowBar, &QWK::WindowBar::closeRequested, this, &QWidget::close);
    auto clockWidget = new ClockWidget();
    clockWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
    connect(clockWidget, &QAbstractButton::clicked, this, [this]() {
        if (!isMaximized()) {
            showMaximized();
        } else {
            showNormal();
        }
    });
    setMenuWidget(windowBar);
    setCentralWidget(clockWidget);
    setWindowTitle("Example MainWindow");
    // setContentsMargins({0, 1, 0, 0});
    resize(640, 480);
}
examples/mainwindow/mainwindow.h
@@ -4,11 +4,15 @@
#include <QtWidgets/QMainWindow>
class MainWindow : public QMainWindow {
    Q_OBJECT
public:
    explicit MainWindow(QWidget *parent = nullptr);
    ~MainWindow();
    ~MainWindow() override;
protected:
    bool event(QEvent *event) override;
private:
    void installWindowAgent();
};
examples/mainwindow/mainwindow.qrc
New file
@@ -0,0 +1,5 @@
<RCC>
    <qresource prefix="/">
        <file>dark-style.qss</file>
    </qresource>
</RCC>
examples/qml/CMakeLists.txt
@@ -7,3 +7,8 @@
    QT_LINKS Core Gui Qml Quick
    LINKS QWKQuick
)
set_target_properties(${PROJECT_NAME} PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED TRUE
)
examples/shared/resources/window-bar.qrc
New file
@@ -0,0 +1,11 @@
<RCC>
    <qresource prefix="/">
        <file>window-bar/close.svg</file>
        <file>window-bar/fullscreen.svg</file>
        <file>window-bar/icon.svg</file>
        <file>window-bar/maximize.svg</file>
        <file>window-bar/minimize.svg</file>
        <file>window-bar/restore.svg</file>
        <file>window-bar/more-line.svg</file>
    </qresource>
</RCC>
examples/shared/resources/window-bar/close.svg
New file
@@ -0,0 +1,15 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" width="10.88" height="10.88"
  viewBox="0 0 10.88 10.88">
  <defs>
    <style>
      .cls-1 {
        fill: none;
        stroke: white;
        stroke-miterlimit: 10;
        stroke-width: 1.25px;
      }
    </style>
  </defs>
  <line class="cls-1" x1="0.44" y1="0.44" x2="10.44" y2="10.44" />
  <line class="cls-1" x1="0.44" y1="10.44" x2="10.44" y2="0.44" />
</svg>
examples/shared/resources/window-bar/fullscreen.svg
New file
@@ -0,0 +1,11 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1594017175519"
    class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="1933"
    xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
    <defs>
        <style type="text/css"></style>
    </defs>
    <path
        d="M874.666667 128h-170.666667a21.333333 21.333333 0 0 0 0 42.666667h119.168l-176.917333 176.917333a21.333333 21.333333 0 1 0 30.165333 30.165333L853.333333 200.832V320a21.333333 21.333333 0 0 0 42.666667 0V149.333333a21.333333 21.333333 0 0 0-21.333333-21.333333zM347.584 646.250667L170.666667 823.168V704a21.333333 21.333333 0 0 0-42.666667 0v170.666667a21.333333 21.333333 0 0 0 21.333333 21.333333h170.666667a21.333333 21.333333 0 0 0 0-42.666667H200.832l176.917333-176.917333a21.333333 21.333333 0 0 0-30.165333-30.165333zM874.666667 682.666667a21.333333 21.333333 0 0 0-21.333334 21.333333v119.168l-176.917333-176.917333a21.333333 21.333333 0 0 0-30.165333 30.165333L823.168 853.333333H704a21.333333 21.333333 0 0 0 0 42.666667h170.666667a21.333333 21.333333 0 0 0 21.333333-21.333333v-170.666667a21.333333 21.333333 0 0 0-21.333333-21.333333zM200.832 170.666667H320a21.333333 21.333333 0 0 0 0-42.666667H149.333333a21.333333 21.333333 0 0 0-21.333333 21.333333v170.666667a21.333333 21.333333 0 0 0 42.666667 0V200.832l176.917333 176.917333a21.333333 21.333333 0 0 0 30.165333-30.165333z"
        fill="#ffffff" p-id="1934"></path>
</svg>
examples/shared/resources/window-bar/icon.svg
New file
@@ -0,0 +1,18 @@
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd"><svg t="1597412561411"
    class="icon" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="780"
    xmlns:xlink="http://www.w3.org/1999/xlink" width="16" height="16">
    <defs>
        <style type="text/css"></style>
    </defs>
    <path d="M512 384m-170.666667 0a170.666667 170.666667 0 1 0 341.333334 0 170.666667 170.666667 0 1 0-341.333334 0Z"
        fill="#009688" p-id="781"></path>
    <path d="M341.333333 384h341.333334v128H341.333333z" fill="#E89419" p-id="782"></path>
    <path
        d="M661.333333 106.666667v85.333333h-46.933333c-51.2 0-25.6-21.333333-102.4-21.333333V85.333333c76.8 0 51.2 21.333333 102.4 21.333334H661.333333z"
        fill="#2196F3" p-id="783"></path>
    <path d="M85.333333 512h853.333334v362.666667H85.333333z" fill="#FFB74D" p-id="784"></path>
    <path
        d="M661.333333 725.333333h85.333334v106.666667h-85.333334zM533.333333 725.333333h85.333334v106.666667h-85.333334zM405.333333 725.333333h85.333334v106.666667h-85.333334zM789.333333 725.333333h85.333334v106.666667h-85.333334zM277.333333 725.333333h85.333334v106.666667h-85.333334zM149.333333 725.333333h85.333334v106.666667H149.333333zM661.333333 576h85.333334v106.666667h-85.333334zM533.333333 576h85.333334v106.666667h-85.333334zM405.333333 576h85.333334v106.666667h-85.333334zM618.666667 512h-85.333334v-64c0-23.466667 19.2-42.666667 42.666667-42.666667s42.666667 19.2 42.666667 42.666667v64zM490.666667 512h-85.333334v-64c0-23.466667 19.2-42.666667 42.666667-42.666667s42.666667 19.2 42.666667 42.666667v64zM789.333333 576h85.333334v106.666667h-85.333334zM277.333333 576h85.333334v106.666667h-85.333334zM149.333333 576h85.333334v106.666667H149.333333z"
        fill="#B07424" p-id="785"></path>
</svg>
examples/shared/resources/window-bar/maximize.svg
New file
@@ -0,0 +1,12 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" width="10" height="10" viewBox="0 0 10 10">
  <defs>
    <style>
      .cls-1 {
        fill: none;
        stroke: white;
        stroke-miterlimit: 10;
      }
    </style>
  </defs>
  <rect class="cls-1" x="0.5" y="0.5" width="9" height="9" />
</svg>
examples/shared/resources/window-bar/minimize.svg
New file
@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 24.3.0, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
<svg version="1.1" id="图层_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px"
    y="0px" viewBox="0 0 10 10" style="enable-background:new 0 0 10 10;" xml:space="preserve">
    <style type="text/css">
        .st0 {
            fill: white;
        }
    </style>
    <rect y="4.5" class="st0" width="10" height="1" />
</svg>
examples/shared/resources/window-bar/more-line.svg
New file
@@ -0,0 +1,5 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" width="24" height="24">
    <path fill="none" d="M0 0h24v24H0z" />
    <path fill="white"
        d="M4.5 10.5c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5S6 12.825 6 12s-.675-1.5-1.5-1.5zm15 0c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5S21 12.825 21 12s-.675-1.5-1.5-1.5zm-7.5 0c-.825 0-1.5.675-1.5 1.5s.675 1.5 1.5 1.5 1.5-.675 1.5-1.5-.675-1.5-1.5-1.5z" />
</svg>
examples/shared/resources/window-bar/restore.svg
New file
@@ -0,0 +1,16 @@
<svg id="图层_1" data-name="图层 1" xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 12 12">
  <defs>
    <style>
      .cls-1 {
        fill: none;
        stroke: white;
        stroke-miterlimit: 10;
      }
    </style>
  </defs>
  <rect class="cls-1" x="0.5" y="2.5" width="9" height="9" />
  <line class="cls-1" x1="2.5" y1="2.5" x2="2.5" y2="0.5" />
  <line class="cls-1" x1="12" y1="0.5" x2="2" y2="0.5" />
  <line class="cls-1" x1="11.5" y1="10" x2="11.5" />
  <line class="cls-1" x1="10" y1="9.5" x2="12" y2="9.5" />
</svg>
examples/shared/widgetframe/CMakeLists.txt
@@ -13,4 +13,9 @@
    QT_LINKS Core Gui Widgets
)
set_target_properties(${PROJECT_NAME} PROPERTIES
    CXX_STANDARD 17
    CXX_STANDARD_REQUIRED TRUE
)
target_include_directories(${PROJECT_NAME} PUBLIC . ..)
examples/shared/widgetframe/windowbutton.cpp
New file
@@ -0,0 +1,92 @@
#include "windowbutton.h"
#include "windowbutton_p.h"
#include <QtCore/QDebug>
#include <QtGui/QtEvents>
namespace QWK {
    WindowButtonPrivate::WindowButtonPrivate() {
    }
    WindowButtonPrivate::~WindowButtonPrivate() {
    }
    void WindowButtonPrivate::init() {
    }
    void WindowButtonPrivate::reloadIcon() {
        Q_Q(WindowButton);
        if (!q->isEnabled() && !iconDisabled.isNull()) {
            q->setIcon(iconDisabled);
            return;
        }
        if (q->isChecked() && !iconChecked.isNull()) {
            q->setIcon(iconChecked);
            return;
        }
        if (!iconNormal.isNull()) {
            q->setIcon(iconNormal);
        }
    }
    WindowButton::WindowButton(QWidget *parent) : WindowButton(*new WindowButtonPrivate(), parent) {
    }
    WindowButton::~WindowButton() = default;
    QIcon WindowButton::iconNormal() const {
        Q_D(const WindowButton);
        return d->iconNormal;
    }
    void WindowButton::setIconNormal(const QIcon &icon) {
        Q_D(WindowButton);
        d->iconNormal = icon;
        d->reloadIcon();
    }
    QIcon WindowButton::iconChecked() const {
        Q_D(const WindowButton);
        return d->iconChecked;
    }
    void WindowButton::setIconChecked(const QIcon &icon) {
        Q_D(WindowButton);
        d->iconChecked = icon;
        d->reloadIcon();
    }
    QIcon WindowButton::iconDisabled() const {
        Q_D(const WindowButton);
        return d->iconDisabled;
    }
    void WindowButton::setIconDisabled(const QIcon &icon) {
        Q_D(WindowButton);
        d->iconDisabled = icon;
        d->reloadIcon();
    }
    void WindowButton::checkStateSet() {
        Q_D(WindowButton);
        d->reloadIcon();
    }
    void WindowButton::mouseDoubleClickEvent(QMouseEvent *event) {
        if (event->button() == Qt::LeftButton) {
            Q_EMIT doubleClicked();
        }
    }
    WindowButton::WindowButton(WindowButtonPrivate &d, QWidget *parent)
        : QPushButton(parent), d_ptr(&d) {
        d.q_ptr = this;
        d.init();
    }
}
examples/shared/widgetframe/windowbutton.h
New file
@@ -0,0 +1,48 @@
#ifndef WINDOWBUTTON_H
#define WINDOWBUTTON_H
#include "QPushButton.h"
#include <QtGlobal>
namespace QWK {
    class WindowButtonPrivate;
    class WindowButton : public QPushButton {
        Q_OBJECT
        Q_DECLARE_PRIVATE(WindowButton)
        Q_PROPERTY(QIcon iconNormal READ iconNormal WRITE setIconNormal FINAL)
        Q_PROPERTY(QIcon iconChecked READ iconChecked WRITE setIconChecked FINAL)
        Q_PROPERTY(QIcon iconDisabled READ iconDisabled WRITE setIconDisabled FINAL)
    public:
        explicit WindowButton(QWidget *parent = nullptr);
        ~WindowButton();
    public:
        QIcon iconNormal() const;
        void setIconNormal(const QIcon &icon);
        QIcon iconChecked() const;
        void setIconChecked(const QIcon &icon);
        QIcon iconDisabled() const;
        void setIconDisabled(const QIcon &icon);
    Q_SIGNALS:
        void doubleClicked();
    protected:
        void checkStateSet() override;
        void mouseDoubleClickEvent(QMouseEvent *event) override;
    protected:
        WindowButton(WindowButtonPrivate &d, QWidget *parent = nullptr);
        QScopedPointer<WindowButtonPrivate> d_ptr;
    };
}
#endif // WINDOWBUTTON_H
examples/shared/widgetframe/windowbutton_p.h
New file
@@ -0,0 +1,27 @@
#ifndef WINDOWBUTTONPRIVATE_H
#define WINDOWBUTTONPRIVATE_H
#include "windowbutton.h"
namespace QWK {
    class WindowButtonPrivate {
        Q_DECLARE_PUBLIC(WindowButton)
    public:
        WindowButtonPrivate();
        virtual ~WindowButtonPrivate();
        void init();
        WindowButton *q_ptr;
        QIcon iconNormal;
        QIcon iconChecked;
        QIcon iconDisabled;
        void reloadIcon();
    };
}
#endif // WINDOWBUTTONPRIVATE_H
src/core/contexts/abstractwindowcontext.cpp
@@ -195,6 +195,7 @@
                map.insert(QStringLiteral("inactiveDark"), kSampleColorSet.inactiveDark);
                return;
            }
            default:
                break;
        }
src/core/contexts/win32windowcontext.cpp
@@ -849,6 +849,7 @@
                    QPoint{m_windowHandle->width(), 0}
                });
                painter.restore();
                return;
            }
            default: {
src/widgets/widgetwindowagent_win.cpp
@@ -81,7 +81,7 @@
                        &region,
                    };
                    ctx->virtual_hook(AbstractWindowContext::DrawWindows10BorderHook, args);
                    return true;
                    break;
                }
                case QEvent::WindowStateChange: {