add KGC APP

This commit is contained in:
www-git-cn 2024-05-04 01:24:40 +08:00
parent c165922894
commit 55e020797c
1089 changed files with 240526 additions and 0 deletions

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>LSMinimumSystemVersion</key>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleDevelopmentRegion</key>
<string>${MACOSX_BUNDLE_DEVELOPMENT_REGION}</string>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,37 @@
#include <windows.h>
IDI_ICON1 ICON "${PROJECT_SOURCE_DIR}/resources/imgs/icon.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION ${version_str}
PRODUCTVERSION ${version_str}
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "Made by Mentalflow."
VALUE "CompanyName", "${PROJECT_COMPANY}"
VALUE "FileDescription", "${PROJECT_BUNDLE_NAME}"
VALUE "FileVersion", "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
VALUE "InternalName", "${PROJECT_BUNDLE_NAME}.exe"
VALUE "LegalCopyright", "${PROJECT_COPYRIGHT}"
VALUE "OriginalFilename", "${PROJECT_BUNDLE_NAME}.exe"
VALUE "ProductName", "${PROJECT_BUNDLE_NAME}"
VALUE "ProductVersion", "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@ -0,0 +1,75 @@
name: MacOS (Shared Library)
on:
push:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/macos-shared.yml'
pull_request:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/macos-shared.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-14]
qt_ver: [6.6.3]
qt_arch: [clang_64]
env:
targetName: KGCAPP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Set up Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: build macos
run: |
cmake --version
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/Users/runner/work/KGCAPP/Qt/${{ matrix.qt_ver }}/macos -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -GNinja ..
cmake --build . --target all --config Release --parallel
- name: package
run: |
# 先删除所有dSYM文件减少包的体积
sudo find /Users/runner/work/KGCAPP/Qt/${{ matrix.qt_ver }}/macos/qml -name "*.dSYM" | xargs rm -r
# 拷贝依赖
sudo macdeployqt /Users/runner/work/KGCAPP/KGCAPP/build/output/release/${targetName}.app -qmldir=. -verbose=1 -dmg
- uses: actions/upload-artifact@v4
with:
name: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_shared.zip
path: /Users/runner/work/KGCAPP/KGCAPP/build/output/release/${{ env.targetName }}.dmg
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: /Users/runner/work/KGCAPP/KGCAPP/build/output/release/${{ env.targetName }}.dmg
asset_name: ${{ env.targetName }}_${{ github.ref_name }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_shared.dmg
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,75 @@
name: MacOS (Static Library)
on:
push:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/macos-static.yml'
pull_request:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/macos-static.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-14]
qt_ver: [6.6.3]
qt_arch: [clang_64]
env:
targetName: KGCAPP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Set up Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: build macos
run: |
cmake --version
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/Users/runner/work/KGCAPP/Qt/${{ matrix.qt_ver }}/macos -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -DKGCAPP_BUILD_STATIC_LIB=ON -GNinja ..
cmake --build . --target all --config Release --parallel
- name: package
run: |
# 先删除所有dSYM文件减少包的体积
sudo find /Users/runner/work/KGCAPP/Qt/${{ matrix.qt_ver }}/macos/qml -name "*.dSYM" | xargs rm -r
# 拷贝依赖
sudo macdeployqt /Users/runner/work/KGCAPP/KGCAPP/build/output/release/${targetName}.app -qmldir=. -verbose=1 -dmg
- uses: actions/upload-artifact@v4
with:
name: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_static.zip
path: /Users/runner/work/KGCAPP/KGCAPP/build/output/release/${{ env.targetName }}.dmg
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: /Users/runner/work/KGCAPP/KGCAPP/build/output/release/${{ env.targetName }}.dmg
asset_name: ${{ env.targetName }}_${{ github.ref_name }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static.dmg
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,92 @@
name: Ubuntu (Shared Library)
on:
workflow_dispatch:
push:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/ubuntu-shared.yml'
pull_request:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/ubuntu-shared.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
qt_ver: [6.6.3]
qt_arch: [gcc_64]
env:
targetName: KGCAPP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Set up Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: ubuntu install GL library
run: sudo apt install -y libxcb-cursor0 libgl1-mesa-dev libxcb1-dev libgtk-3-dev libxkbcommon-x11-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-xinerama0-dev libxcb-sync-dev libxcb-render-util0-dev libxcb-shm0-dev
- name: ubuntu install libfuse2
run: sudo apt install libfuse2
- name: build ubuntu
run: |
ninja --version
cmake --version
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/home/runner/work/KGCAPP/Qt/${{ matrix.qt_ver }}/gcc_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -GNinja ..
cmake --build . --target all --config Release --parallel
- name: install QT linux deploy
uses: miurahr/install-linuxdeploy-action@v1
with:
plugins: qt appimage
- name: Check if svg file exists
run: if [ ! -f "${targetName}.svg" ]; then echo "File not found, creating..."; touch ${targetName}.svg; fi
- name: package
run: |
# make sure Qt plugin finds QML sources so it can deploy the imported files
export QML_SOURCES_PATHS=./
# 拷贝依赖
linuxdeploy-x86_64.AppImage --plugin=qt --output=appimage --create-desktop-file --icon-file=${targetName}.svg --executable=/home/runner/work/KGCAPP/KGCAPP/build/output/release/${targetName} --appdir /home/runner/work/KGCAPP/KGCAPP/build/output/release/
mv ${{ env.targetName }}-*.AppImage ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_shared.AppImage
- uses: actions/upload-artifact@v4
with:
name: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_shared
path: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_shared.AppImage
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_shared.AppImage
asset_name: ${{ env.targetName }}_${{ github.ref_name }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_shared.AppImage
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,92 @@
name: Ubuntu (Static Library)
on:
workflow_dispatch:
push:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/ubuntu-static.yml'
pull_request:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/ubuntu-static.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
qt_ver: [6.6.3]
qt_arch: [gcc_64]
env:
targetName: KGCAPP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Set up Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: ubuntu install GL library
run: sudo apt-get install -y libxcb-cursor0 libgl1-mesa-dev libxcb1-dev libgtk-3-dev libxkbcommon-x11-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-xinerama0-dev libxcb-sync-dev libxcb-render-util0-dev libxcb-shm0-dev
- name: ubuntu install libfuse2
run: sudo apt install libfuse2
- name: build ubuntu
run: |
ninja --version
cmake --version
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/home/runner/work/KGCAPP/Qt/${{ matrix.qt_ver }}/gcc_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -DKGCAPP_BUILD_STATIC_LIB=ON -GNinja ..
cmake --build . --target all --config Release --parallel
- name: install QT linux deploy
uses: miurahr/install-linuxdeploy-action@v1
with:
plugins: qt appimage
- name: Check if svg file exists
run: if [ ! -f "${targetName}.svg" ]; then echo "File not found, creating..."; touch ${targetName}.svg; fi
- name: package
run: |
# make sure Qt plugin finds QML sources so it can deploy the imported files
export QML_SOURCES_PATHS=./
# 拷贝依赖
linuxdeploy-x86_64.AppImage --plugin=qt --output=appimage --create-desktop-file --icon-file=${targetName}.svg --executable=/home/runner/work/KGCAPP/KGCAPP/build/output/release/${targetName} --appdir /home/runner/work/KGCAPP/KGCAPP/build/output/release/
mv ${{ env.targetName }}-*.AppImage ${{ env.targetName }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static.AppImage
- uses: actions/upload-artifact@v4
with:
name: ${{ env.targetName }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static
path: ${{ env.targetName }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static.AppImage
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ env.targetName }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static.AppImage
asset_name: ${{ env.targetName }}_${{ github.ref_name }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static.AppImage
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,95 @@
name: Windows MinGW (Shared Library)
on:
push:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/windows-mingw-shared.yml'
pull_request:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/windows-mingw-shared.yml'
jobs:
build:
name: Build
runs-on: windows-latest
strategy:
matrix:
include:
- qt_arch: win64_mingw
qt_ver: 6.6.3
qt_tools: "tools_mingw,9.0.0-1-202203221220,qt.tools.win64_mingw900"
qt_tools_mingw_install: mingw900_64
env:
targetName: KGCAPP.exe
fileName: KGCAPP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Qt6 environment configuration
if: ${{ startsWith( matrix.qt_ver, 6 ) }}
shell: pwsh
run: |
Write-Output "${{ env.Qt6_DIR }}/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
Write-Output "${{ env.Qt6_DIR }}/../../Tools/${{ matrix.qt_tools_mingw_install }}/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: where is cmake & where is mingw32-make
shell: pwsh
run: |
Get-Command -Name 'cmake' | Format-List
Get-Command -Name 'mingw32-make' | Format-List
- name: mingw-build
id: build
shell: cmd
run: |
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\KGCAPP\Qt\${{ matrix.qt_ver }}\mingw_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -GNinja ..
cmake --build . --target all --config Release --parallel
- name: package
id: package
env:
archiveName: ${{ env.fileName }}-${{ matrix.qt_arch }}-${{ matrix.qt_ver }}-shared
shell: pwsh
run: |
& scripts\windows-mingw-publish.ps1 ${env:archiveName} ${env:targetName}
$name = ${env:archiveName}
echo "packageName=$name" >> $env:GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.packageName }}
path: ${{ steps.package.outputs.packageName }}
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ steps.package.outputs.packageName }}.zip
asset_name: ${{ env.fileName }}_${{ github.ref_name }}_${{ matrix.qt_arch }}_Qt${{ matrix.qt_ver }}_shared.zip
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,95 @@
name: Windows MinGW (Static Library)
on:
push:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/windows-mingw-static.yml'
pull_request:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/windows-mingw-static.yml'
jobs:
build:
name: Build
runs-on: windows-latest
strategy:
matrix:
include:
- qt_arch: win64_mingw
qt_ver: 6.6.3
qt_tools: "tools_mingw,9.0.0-1-202203221220,qt.tools.win64_mingw900"
qt_tools_mingw_install: mingw900_64
env:
targetName: KGCAPP.exe
fileName: KGCAPP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Qt6 environment configuration
if: ${{ startsWith( matrix.qt_ver, 6 ) }}
shell: pwsh
run: |
Write-Output "${{ env.Qt6_DIR }}/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
Write-Output "${{ env.Qt6_DIR }}/../../Tools/${{ matrix.qt_tools_mingw_install }}/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: where is cmake & where is mingw32-make
shell: pwsh
run: |
Get-Command -Name 'cmake' | Format-List
Get-Command -Name 'mingw32-make' | Format-List
- name: mingw-build
id: build
shell: cmd
run: |
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\KGCAPP\Qt\${{ matrix.qt_ver }}\mingw_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -DKGCAPP_BUILD_STATIC_LIB=ON -GNinja ..
cmake --build . --target all --config Release --parallel
- name: package
id: package
env:
archiveName: ${{ env.fileName }}-${{ matrix.qt_arch }}-${{ matrix.qt_ver }}-static
shell: pwsh
run: |
& scripts\windows-mingw-publish.ps1 ${env:archiveName} ${env:targetName}
$name = ${env:archiveName}
echo "packageName=$name" >> $env:GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.packageName }}
path: ${{ steps.package.outputs.packageName }}
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ steps.package.outputs.packageName }}.zip
asset_name: ${{ env.fileName }}_${{ github.ref_name }}_${{ matrix.qt_arch }}_Qt${{ matrix.qt_ver }}_static.zip
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,89 @@
name: Windows MSVC (Shared Library)
on:
push:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/windows-msvc-shared.yml'
pull_request:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/windows-msvc-shared.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest]
include:
- qt_ver: 6.6.3
qt_arch: win64_msvc2019_64
msvc_arch: x64
qt_arch_install: msvc2019_64
env:
targetName: KGCAPP.exe
fileName: KGCAPP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: msvc-build
id: build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.msvc_arch }}
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\KGCAPP\Qt\${{ matrix.qt_ver }}\msvc2019_64 -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -GNinja ..
cmake --build . --target all --config Release --parallel
echo winSdkDir=%WindowsSdkDir% >> %GITHUB_ENV%
echo winSdkVer=%WindowsSdkVersion% >> %GITHUB_ENV%
echo vcToolsInstallDir=%VCToolsInstallDir% >> %GITHUB_ENV%
echo vcToolsRedistDir=%VCToolsRedistDir% >> %GITHUB_ENV%
- name: package
id: package
env:
archiveName: ${{ env.fileName }}-${{ matrix.qt_arch }}-${{ matrix.qt_ver }}-shared
msvcArch: ${{ matrix.msvc_arch }}
shell: pwsh
run: |
& scripts\windows-publish.ps1 ${env:archiveName} ${env:targetName}
$name = ${env:archiveName}
echo "packageName=$name" >> $env:GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.packageName }}
path: ${{ steps.package.outputs.packageName }}
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ steps.package.outputs.packageName }}.zip
asset_name: ${{ env.fileName }}_${{ github.ref_name }}_${{ matrix.qt_arch }}_Qt${{ matrix.qt_ver }}_shared.zip
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,89 @@
name: Windows MSVC (Static Library)
on:
push:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/windows-msvc-static.yml'
pull_request:
paths:
- '*.txt'
- 'include/**'
- 'source/**'
- 'scripts/**'
- '.github/workflows/windows-msvc-static.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest]
include:
- qt_ver: 6.6.3
qt_arch: win64_msvc2019_64
msvc_arch: x64
qt_arch_install: msvc2019_64
env:
targetName: KGCAPP.exe
fileName: KGCAPP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: msvc-build
id: build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.msvc_arch }}
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\KGCAPP\Qt\${{ matrix.qt_ver }}\msvc2019_64 -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -DKGCAPP_BUILD_STATIC_LIB=ON -GNinja ..
cmake --build . --target all --config Release --parallel
echo winSdkDir=%WindowsSdkDir% >> %GITHUB_ENV%
echo winSdkVer=%WindowsSdkVersion% >> %GITHUB_ENV%
echo vcToolsInstallDir=%VCToolsInstallDir% >> %GITHUB_ENV%
echo vcToolsRedistDir=%VCToolsRedistDir% >> %GITHUB_ENV%
- name: package
id: package
env:
archiveName: ${{ env.fileName }}-${{ matrix.qt_arch }}-${{ matrix.qt_ver }}-static
msvcArch: ${{ matrix.msvc_arch }}
shell: pwsh
run: |
& scripts\windows-publish.ps1 ${env:archiveName} ${env:targetName}
$name = ${env:archiveName}
echo "packageName=$name" >> $env:GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.packageName }}
path: ${{ steps.package.outputs.packageName }}
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ steps.package.outputs.packageName }}.zip
asset_name: ${{ env.fileName }}_${{ github.ref_name }}_${{ matrix.qt_arch }}_Qt${{ matrix.qt_ver }}_static.zip
tag: ${{ github.ref }}
overwrite: true

87
KGCAPP/.gitignore vendored Normal file
View File

@ -0,0 +1,87 @@
# C++ objects and libs
*.slo
*.lo
*.o
*.a
*.la
*.lai
#*.so
#*.so.*
#*.dll
#*.dylib
# Qt-es
object_script.*.Release
object_script.*.Debug
*_plugin_import.cpp
/.qmake.cache
/.qmake.stash
*.pro.user
*.pro.user.*
*.qbs.user
*.qbs.user.*
*.moc
moc_*.cpp
moc_*.h
qrc_*.cpp
ui_*.h
*.qmlc
*.jsc
Makefile*
*build-*
*.qm
*.prl
# Qt unit tests
target_wrapper.*
# QtCreator
*.autosave
# QtCreator Qml
*.qmlproject.user
*.qmlproject.user.*
# QtCreator CMake
CMakeLists.txt.user*
# QtCreator 4.8< compilation database
compile_commands.json
# QtCreator local machine specific files for imported projects
*creator.user*
*_qmlcache.qrc
# ---> macOS
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# macOS
.DS_Store
*/.DS_Store

View File

@ -0,0 +1,75 @@
name: MacOS (Shared Library)
on:
push:
paths:
- '*.txt'
- 'example/**'
- 'lib_source/**'
- 'scripts/**'
- '.github/workflows/macos-shared.yml'
pull_request:
paths:
- '*.txt'
- 'example/**'
- 'lib_source/**'
- 'scripts/**'
- '.github/workflows/macos-shared.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-14]
qt_ver: [6.6.3]
qt_arch: [clang_64]
env:
targetName: RibbonUI-APP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Set up Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: build macos
run: |
cmake --version
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/Users/runner/work/RibbonUI/Qt/${{ matrix.qt_ver }}/macos -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -GNinja ..
cmake --build . --target all --config Release --parallel
- name: package
run: |
# 先删除所有dSYM文件减少包的体积
sudo find /Users/runner/work/RibbonUI/Qt/${{ matrix.qt_ver }}/macos/qml -name "*.dSYM" | xargs rm -r
# 拷贝依赖
sudo macdeployqt /Users/runner/work/RibbonUI/RibbonUI/build/app/release/${targetName}.app -qmldir=. -verbose=1 -dmg
- uses: actions/upload-artifact@v4
with:
name: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_shared.zip
path: /Users/runner/work/RibbonUI/RibbonUI/build/app/release/${{ env.targetName }}.dmg
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: /Users/runner/work/RibbonUI/RibbonUI/build/app/release/${{ env.targetName }}.dmg
asset_name: ${{ env.targetName }}_${{ github.ref_name }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_shared.dmg
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,75 @@
name: MacOS (Static Library)
on:
push:
paths:
- '*.txt'
- 'example/**'
- 'lib_source/**'
- 'scripts/**'
- '.github/workflows/macos-static.yml'
pull_request:
paths:
- '*.txt'
- 'example/**'
- 'lib_source/**'
- 'scripts/**'
- '.github/workflows/macos-static.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [macos-14]
qt_ver: [6.6.3]
qt_arch: [clang_64]
env:
targetName: RibbonUI-APP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Set up Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: build macos
run: |
cmake --version
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/Users/runner/work/RibbonUI/Qt/${{ matrix.qt_ver }}/macos -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -DRIBBONUI_BUILD_STATIC_LIB=ON -GNinja ..
cmake --build . --target all --config Release --parallel
- name: package
run: |
# 先删除所有dSYM文件减少包的体积
sudo find /Users/runner/work/RibbonUI/Qt/${{ matrix.qt_ver }}/macos/qml -name "*.dSYM" | xargs rm -r
# 拷贝依赖
sudo macdeployqt /Users/runner/work/RibbonUI/RibbonUI/build/app/release/${targetName}.app -qmldir=. -verbose=1 -dmg
- uses: actions/upload-artifact@v4
with:
name: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_static.zip
path: /Users/runner/work/RibbonUI/RibbonUI/build/app/release/${{ env.targetName }}.dmg
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: /Users/runner/work/RibbonUI/RibbonUI/build/app/release/${{ env.targetName }}.dmg
asset_name: ${{ env.targetName }}_${{ github.ref_name }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static.dmg
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,92 @@
name: Ubuntu (Shared Library)
on:
workflow_dispatch:
push:
paths:
- '*.txt'
- 'lib_source/**'
- 'example/**'
- 'scripts/**'
- '.github/workflows/ubuntu-shared.yml'
pull_request:
paths:
- '*.txt'
- 'lib_source/**'
- 'example/**'
- 'scripts/**'
- '.github/workflows/ubuntu-shared.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
qt_ver: [6.6.3]
qt_arch: [gcc_64]
env:
targetName: RibbonUI-APP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Set up Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: ubuntu install GL library
run: sudo apt install -y libxcb-cursor0 libgl1-mesa-dev libxcb1-dev libgtk-3-dev libxkbcommon-x11-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-xinerama0-dev libxcb-sync-dev libxcb-render-util0-dev libxcb-shm0-dev
- name: ubuntu install libfuse2
run: sudo apt install libfuse2
- name: build ubuntu
run: |
ninja --version
cmake --version
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/home/runner/work/RibbonUI/Qt/${{ matrix.qt_ver }}/gcc_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -GNinja ..
cmake --build . --target all --config Release --parallel
- name: install QT linux deploy
uses: miurahr/install-linuxdeploy-action@v1
with:
plugins: qt appimage
- name: Check if svg file exists
run: if [ ! -f "${targetName}.svg" ]; then echo "File not found, creating..."; touch ${targetName}.svg; fi
- name: package
run: |
# make sure Qt plugin finds QML sources so it can deploy the imported files
export QML_SOURCES_PATHS=./
# 拷贝依赖
linuxdeploy-x86_64.AppImage --plugin=qt --output=appimage --create-desktop-file --icon-file=${targetName}.svg --executable=/home/runner/work/RibbonUI/RibbonUI/build/app/release/${targetName} --appdir /home/runner/work/RibbonUI/RibbonUI/build/app/release/
mv ${{ env.targetName }}-*.AppImage ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_shared.AppImage
- uses: actions/upload-artifact@v4
with:
name: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_shared
path: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_shared.AppImage
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ env.targetName }}_${{ matrix.os }}_${{matrix.qt_ver}}_shared.AppImage
asset_name: ${{ env.targetName }}_${{ github.ref_name }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_shared.AppImage
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,92 @@
name: Ubuntu (Static Library)
on:
workflow_dispatch:
push:
paths:
- '*.txt'
- 'lib_source/**'
- 'example/**'
- 'scripts/**'
- '.github/workflows/ubuntu-static.yml'
pull_request:
paths:
- '*.txt'
- 'lib_source/**'
- 'example/**'
- 'scripts/**'
- '.github/workflows/ubuntu-static.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [ubuntu-latest]
qt_ver: [6.6.3]
qt_arch: [gcc_64]
env:
targetName: RibbonUI-APP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Set up Ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: ubuntu install GL library
run: sudo apt-get install -y libxcb-cursor0 libgl1-mesa-dev libxcb1-dev libgtk-3-dev libxkbcommon-x11-dev libxcb-icccm4-dev libxcb-image0-dev libxcb-keysyms1-dev libxcb-randr0-dev libxcb-shape0-dev libxcb-xfixes0-dev libxcb-xinerama0-dev libxcb-sync-dev libxcb-render-util0-dev libxcb-shm0-dev
- name: ubuntu install libfuse2
run: sudo apt install libfuse2
- name: build ubuntu
run: |
ninja --version
cmake --version
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/home/runner/work/RibbonUI/Qt/${{ matrix.qt_ver }}/gcc_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -DRIBBONUI_BUILD_STATIC_LIB=ON -GNinja ..
cmake --build . --target all --config Release --parallel
- name: install QT linux deploy
uses: miurahr/install-linuxdeploy-action@v1
with:
plugins: qt appimage
- name: Check if svg file exists
run: if [ ! -f "${targetName}.svg" ]; then echo "File not found, creating..."; touch ${targetName}.svg; fi
- name: package
run: |
# make sure Qt plugin finds QML sources so it can deploy the imported files
export QML_SOURCES_PATHS=./
# 拷贝依赖
linuxdeploy-x86_64.AppImage --plugin=qt --output=appimage --create-desktop-file --icon-file=${targetName}.svg --executable=/home/runner/work/RibbonUI/RibbonUI/build/app/release/${targetName} --appdir /home/runner/work/RibbonUI/RibbonUI/build/app/release/
mv ${{ env.targetName }}-*.AppImage ${{ env.targetName }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static.AppImage
- uses: actions/upload-artifact@v4
with:
name: ${{ env.targetName }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static
path: ${{ env.targetName }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static.AppImage
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ env.targetName }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static.AppImage
asset_name: ${{ env.targetName }}_${{ github.ref_name }}_${{ matrix.os }}_Qt${{ matrix.qt_ver }}_static.AppImage
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,95 @@
name: Windows MinGW (Shared Library)
on:
push:
paths:
- '*.txt'
- 'lib_source/**'
- 'example/**'
- 'scripts/**'
- '.github/workflows/windows-mingw-shared.yml'
pull_request:
paths:
- '*.txt'
- 'example/**'
- 'lib_source/**'
- 'scripts/**'
- '.github/workflows/windows-mingw-shared.yml'
jobs:
build:
name: Build
runs-on: windows-latest
strategy:
matrix:
include:
- qt_arch: win64_mingw
qt_ver: 6.6.3
qt_tools: "tools_mingw,9.0.0-1-202203221220,qt.tools.win64_mingw900"
qt_tools_mingw_install: mingw900_64
env:
targetName: RibbonUI-APP.exe
fileName: RibbonUI-APP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Qt6 environment configuration
if: ${{ startsWith( matrix.qt_ver, 6 ) }}
shell: pwsh
run: |
Write-Output "${{ env.Qt6_DIR }}/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
Write-Output "${{ env.Qt6_DIR }}/../../Tools/${{ matrix.qt_tools_mingw_install }}/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: where is cmake & where is mingw32-make
shell: pwsh
run: |
Get-Command -Name 'cmake' | Format-List
Get-Command -Name 'mingw32-make' | Format-List
- name: mingw-build
id: build
shell: cmd
run: |
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\RibbonUI\Qt\${{ matrix.qt_ver }}\mingw_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -GNinja ..
cmake --build . --target all --config Release --parallel
- name: package
id: package
env:
archiveName: ${{ env.fileName }}-${{ matrix.qt_arch }}-${{ matrix.qt_ver }}-shared
shell: pwsh
run: |
& scripts\windows-mingw-publish.ps1 ${env:archiveName} ${env:targetName}
$name = ${env:archiveName}
echo "packageName=$name" >> $env:GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.packageName }}
path: ${{ steps.package.outputs.packageName }}
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ steps.package.outputs.packageName }}.zip
asset_name: ${{ env.fileName }}_${{ github.ref_name }}_${{ matrix.qt_arch }}_Qt${{ matrix.qt_ver }}_shared.zip
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,95 @@
name: Windows MinGW (Static Library)
on:
push:
paths:
- '*.txt'
- 'lib_source/**'
- 'example/**'
- 'scripts/**'
- '.github/workflows/windows-mingw-static.yml'
pull_request:
paths:
- '*.txt'
- 'example/**'
- 'lib_source/**'
- 'scripts/**'
- '.github/workflows/windows-mingw-static.yml'
jobs:
build:
name: Build
runs-on: windows-latest
strategy:
matrix:
include:
- qt_arch: win64_mingw
qt_ver: 6.6.3
qt_tools: "tools_mingw,9.0.0-1-202203221220,qt.tools.win64_mingw900"
qt_tools_mingw_install: mingw900_64
env:
targetName: RibbonUI-APP.exe
fileName: RibbonUI-APP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: Qt6 environment configuration
if: ${{ startsWith( matrix.qt_ver, 6 ) }}
shell: pwsh
run: |
Write-Output "${{ env.Qt6_DIR }}/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
Write-Output "${{ env.Qt6_DIR }}/../../Tools/${{ matrix.qt_tools_mingw_install }}/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append
- name: where is cmake & where is mingw32-make
shell: pwsh
run: |
Get-Command -Name 'cmake' | Format-List
Get-Command -Name 'mingw32-make' | Format-List
- name: mingw-build
id: build
shell: cmd
run: |
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\RibbonUI\Qt\${{ matrix.qt_ver }}\mingw_64 -DCMAKE_C_COMPILER=gcc -DCMAKE_CXX_COMPILER=g++ -DCMAKE_BUILD_TYPE=Release -DRIBBONUI_BUILD_STATIC_LIB=ON -GNinja ..
cmake --build . --target all --config Release --parallel
- name: package
id: package
env:
archiveName: ${{ env.fileName }}-${{ matrix.qt_arch }}-${{ matrix.qt_ver }}-static
shell: pwsh
run: |
& scripts\windows-mingw-publish.ps1 ${env:archiveName} ${env:targetName}
$name = ${env:archiveName}
echo "packageName=$name" >> $env:GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.packageName }}
path: ${{ steps.package.outputs.packageName }}
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ steps.package.outputs.packageName }}.zip
asset_name: ${{ env.fileName }}_${{ github.ref_name }}_${{ matrix.qt_arch }}_Qt${{ matrix.qt_ver }}_static.zip
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,89 @@
name: Windows MSVC (Shared Library)
on:
push:
paths:
- '*.txt'
- 'lib_source/**'
- 'example/**'
- 'scripts/**'
- '.github/workflows/windows-msvc-shared.yml'
pull_request:
paths:
- '*.txt'
- 'example/**'
- 'lib_source/**'
- 'scripts/**'
- '.github/workflows/windows-msvc-shared.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest]
include:
- qt_ver: 6.6.3
qt_arch: win64_msvc2019_64
msvc_arch: x64
qt_arch_install: msvc2019_64
env:
targetName: RibbonUI-APP.exe
fileName: RibbonUI-APP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: msvc-build
id: build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.msvc_arch }}
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\RibbonUI\Qt\${{ matrix.qt_ver }}\msvc2019_64 -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -GNinja ..
cmake --build . --target all --config Release --parallel
echo winSdkDir=%WindowsSdkDir% >> %GITHUB_ENV%
echo winSdkVer=%WindowsSdkVersion% >> %GITHUB_ENV%
echo vcToolsInstallDir=%VCToolsInstallDir% >> %GITHUB_ENV%
echo vcToolsRedistDir=%VCToolsRedistDir% >> %GITHUB_ENV%
- name: package
id: package
env:
archiveName: ${{ env.fileName }}-${{ matrix.qt_arch }}-${{ matrix.qt_ver }}-shared
msvcArch: ${{ matrix.msvc_arch }}
shell: pwsh
run: |
& scripts\windows-publish.ps1 ${env:archiveName} ${env:targetName}
$name = ${env:archiveName}
echo "packageName=$name" >> $env:GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.packageName }}
path: ${{ steps.package.outputs.packageName }}
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ steps.package.outputs.packageName }}.zip
asset_name: ${{ env.fileName }}_${{ github.ref_name }}_${{ matrix.qt_arch }}_Qt${{ matrix.qt_ver }}_shared.zip
tag: ${{ github.ref }}
overwrite: true

View File

@ -0,0 +1,89 @@
name: Windows MSVC (Static Library)
on:
push:
paths:
- '*.txt'
- 'lib_source/**'
- 'example/**'
- 'scripts/**'
- '.github/workflows/windows-msvc-static.yml'
pull_request:
paths:
- '*.txt'
- 'example/**'
- 'lib_source/**'
- 'scripts/**'
- '.github/workflows/windows-msvc-static.yml'
jobs:
build:
name: Build
runs-on: ${{ matrix.os }}
strategy:
matrix:
os: [windows-latest]
include:
- qt_ver: 6.6.3
qt_arch: win64_msvc2019_64
msvc_arch: x64
qt_arch_install: msvc2019_64
env:
targetName: RibbonUI-APP.exe
fileName: RibbonUI-APP
steps:
- name: Check out repository
uses: actions/checkout@v4
with:
submodules: recursive
- name: Setup ninja
uses: seanmiddleditch/gha-setup-ninja@v4
with:
version: 1.10.2
- name: Install Qt
uses: jurplel/install-qt-action@v3
with:
version: ${{ matrix.qt_ver }}
arch: ${{ matrix.qt_arch }}
modules: 'qt5compat qtmultimedia qtshadertools qtimageformats'
- name: msvc-build
id: build
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvarsall.bat" ${{ matrix.msvc_arch }}
mkdir build
cd build
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=D:\a\RibbonUI\Qt\${{ matrix.qt_ver }}\msvc2019_64 -DCMAKE_C_COMPILER=cl -DCMAKE_CXX_COMPILER=cl -DCMAKE_BUILD_TYPE=Release -DRIBBONUI_BUILD_STATIC_LIB=ON -GNinja ..
cmake --build . --target all --config Release --parallel
echo winSdkDir=%WindowsSdkDir% >> %GITHUB_ENV%
echo winSdkVer=%WindowsSdkVersion% >> %GITHUB_ENV%
echo vcToolsInstallDir=%VCToolsInstallDir% >> %GITHUB_ENV%
echo vcToolsRedistDir=%VCToolsRedistDir% >> %GITHUB_ENV%
- name: package
id: package
env:
archiveName: ${{ env.fileName }}-${{ matrix.qt_arch }}-${{ matrix.qt_ver }}-static
msvcArch: ${{ matrix.msvc_arch }}
shell: pwsh
run: |
& scripts\windows-publish.ps1 ${env:archiveName} ${env:targetName}
$name = ${env:archiveName}
echo "packageName=$name" >> $env:GITHUB_OUTPUT
- uses: actions/upload-artifact@v4
with:
name: ${{ steps.package.outputs.packageName }}
path: ${{ steps.package.outputs.packageName }}
- name: uploadRelease
if: startsWith(github.event.ref, 'refs/tags/')
uses: svenstaro/upload-release-action@v2
with:
repo_token: ${{ secrets.GITHUB_TOKEN }}
file: ${{ steps.package.outputs.packageName }}.zip
asset_name: ${{ env.fileName }}_${{ github.ref_name }}_${{ matrix.qt_arch }}_Qt${{ matrix.qt_ver }}_static.zip
tag: ${{ github.ref }}
overwrite: true

87
KGCAPP/3rdparty/RibbonUI/.gitignore vendored Normal file
View File

@ -0,0 +1,87 @@
# C++ objects and libs
*.slo
*.lo
*.o
*.a
*.la
*.lai
*.so
*.so.*
#*.dll
*.dylib
# Qt-es
object_script.*.Release
object_script.*.Debug
*_plugin_import.cpp
/.qmake.cache
/.qmake.stash
*.pro.user
*.pro.user.*
*.qbs.user
*.qbs.user.*
*.moc
moc_*.cpp
moc_*.h
qrc_*.cpp
ui_*.h
*.qmlc
*.jsc
Makefile*
*build-*
*.qm
*.prl
# Qt unit tests
target_wrapper.*
# QtCreator
*.autosave
# QtCreator Qml
*.qmlproject.user
*.qmlproject.user.*
# QtCreator CMake
CMakeLists.txt.user*
# QtCreator 4.8< compilation database
compile_commands.json
# QtCreator local machine specific files for imported projects
*creator.user*
*_qmlcache.qrc
# ---> macOS
# General
.DS_Store
.AppleDouble
.LSOverride
# Icon must end with two \r
Icon
# Thumbnails
._*
# Files that might appear in the root of a volume
.DocumentRevisions-V100
.fseventsd
.Spotlight-V100
.TemporaryItems
.Trashes
.VolumeIcon.icns
.com.apple.timemachine.donotpresent
# Directories potentially created on remote AFP share
.AppleDB
.AppleDesktop
Network Trash Folder
Temporary Items
.apdisk
# macOS
.DS_Store
*/.DS_Store

3
KGCAPP/3rdparty/RibbonUI/.gitmodules vendored Normal file
View File

@ -0,0 +1,3 @@
[submodule "lib_source/3rdparty/qwindowkit"]
path = lib_source/3rdparty/qwindowkit
url = https://github.com/stdware/qwindowkit.git

28
KGCAPP/3rdparty/RibbonUI/CMakeLists.txt vendored Normal file
View File

@ -0,0 +1,28 @@
cmake_minimum_required(VERSION 3.16)
project(RibbonUI_Project VERSION 1.0.3 LANGUAGES CXX)
find_package(Qt6 REQUIRED COMPONENTS Core)
set(QT_SDK_DIR ${Qt6_DIR}/../../..)
cmake_path(SET QT_SDK_DIR NORMALIZE ${QT_SDK_DIR})
option(RIBBONUI_BUILD_EXAMPLES "Build RibbonUI APP." ON)
option(RIBBONUI_BUILD_QWINDOWKIT "Build QWindowKit." ON)
option(RIBBONUI_BUILD_STATIC_LIB "Build RibbonUI static library." OFF)
if(NOT RIBBONUI_QML_PLUGIN_DIRECTORY)
set(RIBBONUI_QML_PLUGIN_DIRECTORY ${QT_SDK_DIR}/qml/RibbonUI CACHE PATH "RibbonUI Plugin Path")
endif()
add_subdirectory(lib_source)
if (RIBBONUI_BUILD_EXAMPLES)
add_subdirectory(example)
endif()
message("---------------------------- RibbonUI ----------------------------")
message("Build RibbonUI APP: ${RIBBONUI_BUILD_EXAMPLES}")
message("Build RibbonUI static library: ${RIBBONUI_BUILD_STATIC_LIB}")
message("Build QWindowKit: ${RIBBONUI_BUILD_QWINDOWKIT}")
message("RibbonUI QML Plugin Path: ${RIBBONUI_QML_PLUGIN_DIRECTORY}")
message("------------------------------------------------------------------")

21
KGCAPP/3rdparty/RibbonUI/LICENSE vendored Normal file
View File

@ -0,0 +1,21 @@
MIT License
Copyright (c) 2023 Dylan Liu
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@ -0,0 +1,128 @@
# RibbonUI
<div align="center">
<img src="lib_source/resources/imgs/icon.png" alt="Logo" style="width:40%; height:auto;">
</div>
<h1 align="center">Qt RibbonUI </h1>
- [RibbonUI](#ribbonui)
- [1. 介绍](#1-介绍)
- [2. 组件列表](#2-组件列表)
- [3. 支持平台](#3-支持平台)
- [Qt 6 分支 (main 分支)](#qt-6-分支-main-分支)
- [Qt 5 分支 (***TODO***)](#qt-5-分支-todo)
- [4. 如何使用?](#4-如何使用)
- [5. 鸣谢](#5-鸣谢)
- [6. 与我联系](#6-与我联系)
## 1. 介绍
RibbonUI是一个参考微软Ribbon风格即Office 2016后的风格设计的轻量级、简约且优雅的Qt组件库用QML写就。
***[Click to view English README](README.md)***
<div align="center">
<div align="center">
<img src="documents/pictures/home-light-classic.png" alt="Home Light Classic" style="width:45%; height:auto;">
<img src="documents/pictures/home-dark-classic.png" alt="Home Light Classic" style="width:45%; height:auto;">
</div>
<p align="center">主界面浅色/深色主题 (经典风格) </p>
</div>
<div align="center">
<div align="center">
<img src="documents/pictures/home-light-modern.png" alt="Home Light Modern" style="width:45%; height:auto;">
<img src="documents/pictures/home-dark-modern.png" alt="Home Light Modern" style="width:45%; height:auto;">
</div>
<p align="center">主界面浅色/深色主题 (现代风格) </p>
</div>
## 2. 组件列表
目前支持***30***种组件,后续会添加更多。
| 名称 | 介绍 | 展示图片|
|:----:|:----:|:----:|
| RibbonWindow | 基于framelesshelper实现的无边框窗口支持退出确认弹窗及模糊/亚克力化背景。|![RibbonWindow](documents/pictures/home-light-modern.png)|
| RibbonTabBar | 支持多分组页面切换、自定义右上角工具栏、自由收放的工具栏如同Word的。| ![RibbonTabBar](documents/pictures/RibbonTabBar.png) |
| RibbonTitleBar | 支持自定义背景色、自由添加工具按钮的窗口标题栏针对Windows和macOS有不同的窗口按钮设计。| ![RibbonTitleBar](documents/pictures/RibbonTitleBar.png) |
| RibbonBottomBar | 支持添加自定义工具的底栏。 |![RibbonBottomBar](documents/pictures/RibbonBottomBar.png) |
| RibbonBlur | 为各种组件提供模糊化效果 | ![RibbonBlur](documents/pictures/RibbonBlur.png) |
| RibbonButton | 支持有/无背景的纯文字/纯图标/图标加文字的按钮,支持鼠标覆盖显示提示信息 | ![RibbonButton](documents/pictures/RibbonButton.png) |
| RibbonPushButton | 支持弹出菜单的大图标按钮,可以使用内置图标库或用户自选图片 | ![RibbonPushButton](documents/pictures/RibbonPushButton.png) |
| RibbonSlider | 支持水平或垂直放置的滑动控制条 | ![RibbonSlider](documents/pictures/RibbonSlider.png) |
| RibbonIcon | 图标组件,内置图标数百个来自微软的精美图标 | ![RibbonIcon](documents/pictures/RibbonIcon.png) |
| RibbonText | 文字组件,可以允许/禁止选中文字或复制 | ![RibbonText](documents/pictures/RibbonText.png) |
| RibbonCheckBox | 选择框组件,可自定义是否显示文字、文字显示位置(左/右) | ![RibbonCheckBox](documents/pictures/RibbonCheckBox.png) |
| RibbonSwitchButton | 开关按钮,支持自定义背景颜色、是否显示提示文字或按钮文字,可自由调整文字显示位置(左/右) | ![RibbonSwitchButton](documents/pictures/RibbonSwitchButton.png) |
| RibbonTheme | 主题支持浅色、深色、跟随系统三种模式 | ![RibbonTheme](documents/pictures/RibbonTheme.png) |
| RibbonMenu | 菜单组件,支持二元选择及子菜单 | ![RibbonMenu](documents/pictures/RibbonMenu.png) |
| RibbonPopup | 自动居中的弹出式窗口 | ![RibbonPopup](documents/pictures/RibbonPopup.png) |
| RibbonPopDialog | 支持二元或三元选择的弹出式窗口 | ![RibbonPopDialog](documents/pictures/RibbonPopDialog.png) |
| RibbonLineEdit | 支持设置图标和一键清除的单行文本输入控件 | ![RibbonLineEdit](documents/pictures/RibbonLineEdit.png) |
| RibbonTextEdit | 支持设置图标和一键清除的多行文本输入控件,可随文本输入自动换行/滚动 | ![RibbonTextEdit](documents/pictures/RibbonTextEdit.png) |
| RibbonTextEdit | 支持设置图标和一键清除的多行文本输入控件,可随文本输入自动换行/滚动 | ![RibbonTextEdit](documents/pictures/RibbonTextEdit.png) |
| RibbonComboBox | 支持设置图标的下拉菜单选择控件,菜单支持用户输入添加 | ![RibbonComboBox](documents/pictures/RibbonComboBox.png) |
| RibbonSpinBox | 支持设置图标的旋钮控件 | ![RibbonSpinBox](documents/pictures/RibbonSpinBox.png) |
| RibbonSpinBox | 支持设置图标的旋钮控件 | ![RibbonSpinBox](documents/pictures/RibbonSpinBox.png) |
| RibbonView | 与TabBar和BottomBar搭配使用的可滑动组件两组件模糊化的背景均来源于它 | ![RibbonView](documents/pictures/RibbonView.png) |
| RibbonPaperView | 类似Word中纸张的组件 | ![RibbonPaperView](documents/pictures/RibbonPaperView.png) |
其他组件的介绍会陆续更新。
## 3. 支持平台
目前是基于Qt 6 设计的之后有时间会加入Qt 5的支持, ***因此目前仅支持Qt 6支持的平台***。
### Qt 6 分支 (main 分支)
+ Windows: Windows 10 (1809+), Windows 11.(X86/AMD64, aarch64)
+ macOS: macOS 11+.(AMD64, aarch64)
+ Linux: Ubuntu 22.04+ (X86/AMD64)
### Qt 5 分支 (***TODO***)
+ Windows: Windows 7+.(X86/AMD64)
+ macOS: MacOS X 10.13 - 10.15, macOS 11+.(AMD64, aarch64)
+ Linux: Ubuntu 18.04+ (X86/AMD64)
## 4. 如何使用?
+ 安装前准备
安装Qt 6推荐通过官方在线安装器安装**Qt 6.6.3版本****通过brew等方式安装可能会出现问题**。**Qt Quick相关模块**和**qt5compat qtshadertools qtimageformats**组件是必要的。
+ 编译例程和库
- 下载仓库并进入
```shell
git clone https://github.com/mentalfl0w/RibbonUI.git --recursive
cd RibbonUI
```
- 创建并进入build文件夹
```shell
mkdir build
cd build
```
- 编译
```shell
# 如果想进行Debug编译, 请配置 -DCMAKE_BUILD_TYPE=Debug和--config Debug参数
# 如果想编译静态库, 请在cmake参数中加入-DRIBBONUI_BUILD_STATIC_LIB=ON
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/Users/runner/work/RibbonUI/Qt/6.6.3/macos -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -GNinja ..
# -DCMAKE_PREFIX_PATH={你的Qt安装目录}
cmake --build . --target all --config Release --parallel
```
+ 在其他项目中使用RibbonUI
- 遵循***编译例程和库***的下载仓库和进入build目录步骤
- 在CMakeLists.txt中加入以下语句
```camke
add_subdirectory(RibbonUI) # RibbonUI在你文件系统中的相对路径
```
- 编译
```shell
# 如果想进行Debug编译, 请配置 -DCMAKE_BUILD_TYPE=Debug和--config Debug参数
# 如果想编译静态库, 请在cmake参数中加入-DRIBBONUI_BUILD_STATIC_LIB=ON
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/Users/runner/work/RibbonUI/Qt/6.6.3/macos -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -DRIBBONUI_BUILD_EXAMPLES=OFF -GNinja ..
# -DCMAKE_PREFIX_PATH={你的Qt安装目录}
cmake --build . --target all --config Release --parallel
```
## 5. 鸣谢
+ 感谢[@wangwenx190](https://github.com/wangwenx190)的[framelesshelper](https://github.com/wangwenx190/framelesshelper)让RibbonWindow能实现无边框。
+ [@Microsoft](https://github.com/microsoft)的[fluentui-system-icons](https://github.com/microsoft/fluentui-system-icons)提供的漂亮图标库.
+ 感谢[@zhuzichu520](https://github.com/zhuzichu520)的[FluentUI](https://github.com/zhuzichu520/FluentUI) 为我提供的灵感和参考。
## 6. 与我联系
+ Email: mentalflow@ourdocs.cn
+ Blog: [菜鸟技术猿的折腾史.](https://blog.ourdocs.cn)
+ ***欢迎发起PR或Issues我会在空余时间尽快完善功能或修复bug希望能一起让RibbonUI变得更好尽情享受吧🎉***

126
KGCAPP/3rdparty/RibbonUI/README.md vendored Normal file
View File

@ -0,0 +1,126 @@
# RibbonUI
<div align="center">
<img src="lib_source/resources/imgs/icon.png" alt="Logo" style="width:40%; height:auto;">
</div>
<h1 align="center">Qt RibbonUI </h1>
- [RibbonUI](#ribbonui)
- [1. Introduction](#1-introduction)
- [2. Included Components](#2-included-components)
- [3. Supported platforms](#3-supported-platforms)
- [Qt 6 branch (main branch)](#qt-6-branch-main-branch)
- [Qt 5 branch (***TODO***)](#qt-5-branch-todo)
- [4. How to use it?](#4-how-to-use-it)
- [5. Acknowledgement](#5-acknowledgement)
- [6. Get in touch with me](#6-get-in-touch-with-me)
## 1. Introduction
RibbonUI is a lightweight, minimalist and elegant Qt component library written in QML and designed with reference to the Microsoft Ribbon style.
***[点击查看中文文档](README(zh-cn).md)***
<div align="center">
<div align="center">
<img src="documents/pictures/home-light-classic.png" alt="Home Light Classic" style="width:45%; height:auto;">
<img src="documents/pictures/home-dark-classic.png" alt="Home Light Classic" style="width:45%; height:auto;">
</div>
<p align="center">Home Light/Dark Theme (Classic Style) </p>
</div>
<div align="center">
<div align="center">
<img src="documents/pictures/home-light-modern.png" alt="Home Light Modern" style="width:45%; height:auto;">
<img src="documents/pictures/home-dark-modern.png" alt="Home Light Modern" style="width:45%; height:auto;">
</div>
<p align="center">Home Light/Dark Theme (Modern Style) </p>
</div>
## 2. Included Components
Currently supports ***30*** components, more will be added later.
| Components | Introduction | Demo Picture|
|:----:|:----:|:----:|
| RibbonWindow | A Window component that relies on framelesshelper to support exit confirmation popups and blur style backgrounds. |![RibbonWindow](documents/pictures/home-light-modern.png)|
| RibbonTabBar | A toolbar with support for page switching and retracting, and support for placing customized buttons in the upper right corner, just like Microsoft Word's. | ![RibbonTabBar](documents/pictures/RibbonTabBar.png) |
| RibbonTitleBar | A window title bar that supports custom colors and the free addition of secondary buttons, with different designs for Windows and macOS. | ![RibbonTitleBar](documents/pictures/RibbonTitleBar.png) |
| RibbonBottomBar | A bottom bar that supports adding custom tools. |![RibbonBottomBar](documents/pictures/RibbonBottomBar.png) |
| RibbonBlur | Provides blurring effects for various components | ![RibbonBlur](documents/pictures/RibbonBlur.png) |
| RibbonButton | A button that supports plain text/plain icons/icons and text with/without backgrounds, with mouse overlay support for displaying alert messages | ![RibbonButton](documents/pictures/RibbonButton.png) |
| RibbonPushButton | A large icon button that supports pop-up menus, either using the built-in icon library or a user-selected image | ![RibbonPushButton](documents/pictures/RibbonPushButton.png) |
| RibbonSlider | Support for horizontally or vertically placed slider | ![RibbonSlider](documents/pictures/RibbonSlider.png) |
| RibbonIcon | Icon component, built-in hundreds of beautiful icons from Microsoft | ![RibbonIcon](documents/pictures/RibbonIcon.png) |
| RibbonText | Text component that allows/disallows selection of text or copying | ![RibbonText](documents/pictures/RibbonText.png) |
| RibbonCheckBox | Checkbox component, customizable whether to display text, where to display text (left/right) | ![RibbonCheckBox](documents/pictures/RibbonCheckBox.png) |
| RibbonSwitchButton | Switch button, support for customizing the background color, whether to display the prompt text or button text, free to adjust the text display position (left/right) | ![RibbonSwitchButton](documents/pictures/RibbonSwitchButton.png) |
| RibbonTheme | Theme support light color, dark color, follow system three modes | ![RibbonTheme](documents/pictures/RibbonTheme.png) |
| RibbonMenu | Menu component with binary selection and submenu support | ![RibbonMenu](documents/pictures/RibbonMenu.png) |
| RibbonPopup | Auto-centered pop-ups | ![RibbonPopup](documents/pictures/RibbonPopup.png) |
| RibbonPopDialog | Popups supporting binary or ternary selection | ![RibbonPopDialog](documents/pictures/RibbonPopDialog.png) |
| RibbonLineEdit | Single-line text input control with support for icons and one-click clearing | ![RibbonLineEdit](documents/pictures/RibbonLineEdit.png) |
| RibbonTextEdit | Multi-line text input control with support for icons and one-click clearing, automatic line feed/scrolling as text is entered | ![RibbonTextEdit](documents/pictures/RibbonTextEdit.png) |
| RibbonComboBox | Supports drop-down menu selection controls with icons, and menus can be added with user input. | ![RibbonComboBox](documents/pictures/RibbonComboBox.png) |
| RibbonSpinBox | SpinBox with support for setting icons | ![RibbonSpinBox](documents/pictures/RibbonSpinBox.png) |
| RibbonView | Slidable component for use with TabBar and BottomBar, from which the blurred backgrounds of both components are derived. | ![RibbonView](documents/pictures/RibbonView.png) |
| RibbonPaperView | Components similar to paper in Word | ![RibbonPaperView](documents/pictures/RibbonPaperView.png) |
***The introduction of other components will be updated later.***
## 3. Supported platforms
The current design is based on Qt 6, and support for Qt 5 will be added sometime later, ***so the current support list is consistent with Qt 6***.
### Qt 6 branch (main branch)
+ Windows: Windows 10 (1809+), Windows 11.(X86/AMD64, aarch64)
+ macOS: macOS 11+.(AMD64, aarch64)
+ Linux: Ubuntu 22.04+ (X86/AMD64)
### Qt 5 branch (***TODO***)
+ Windows: Windows 7+.(X86/AMD64)
+ macOS: MacOS X 10.13 - 10.15, macOS 11+.(AMD64, aarch64)
+ Linux: Ubuntu 18.04+ (X86/AMD64)
## 4. How to use it?
+ Before Install
To install Qt 6, it is recommended to install the **Qt 6.6.3** version via the official online installer, **installing via brew, etc. may cause problems**. **Qt Quick related modules** and the **qt5compat qtshadertools qtimageformats** component are required
+ Compile the example and library
- Clone and enter the library folder
```shell
git clone https://github.com/mentalfl0w/RibbonUI.git --recursive
cd RibbonUI
```
- Create and enter the build folder
```shell
mkdir build
cd build
```
- Build
```shell
# if you want to make a Debug build, just let -DCMAKE_BUILD_TYPE=Debug, --config Debug
# if you want a static build, add -DRIBBONUI_BUILD_STATIC_LIB=ON into command
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/Users/runner/work/RibbonUI/Qt/6.6.3/macos -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -GNinja ..
# -DCMAKE_PREFIX_PATH={YOUR QT INSTALL FOLDER}
cmake --build . --target all --config Release --parallel
```
+ Use library with other project
- Follow the same steps like clone and enter build folder as ***Compile the example and library***
- Add the following code to your CMakeLists.txt
```camke
add_subdirectory(RibbonUI) # RibbonUI's path in your project file system
```
- Build
```shell
# if you want to make a Debug build, just let -DCMAKE_BUILD_TYPE=Debug, --config Debug
# if you want a static build, add -DRIBBONUI_BUILD_STATIC_LIB=ON into command
cmake -DCMAKE_MESSAGE_LOG_LEVEL=STATUS -DCMAKE_PREFIX_PATH=/Users/runner/work/RibbonUI/Qt/6.6.3/macos -DCMAKE_C_COMPILER=clang -DCMAKE_CXX_COMPILER=clang++ -DCMAKE_BUILD_TYPE=Release -DRIBBONUI_BUILD_EXAMPLES=OFF -GNinja ..
# -DCMAKE_PREFIX_PATH={YOUR QT INSTALL FOLDER}
cmake --build . --target all --config Release --parallel
```
## 5. Acknowledgement
+ [@wangwenx190](https://github.com/wangwenx190)'s [framelesshelper](https://github.com/wangwenx190/framelesshelper) for frameless window (aka RibbonWindow's base).
+ [@Microsoft](https://github.com/microsoft)'s [fluentui-system-icons](https://github.com/microsoft/fluentui-system-icons) for beautifully designed icons.
+ [@zhuzichu520](https://github.com/zhuzichu520)'s [FluentUI](https://github.com/zhuzichu520/FluentUI) for inspiration and reference.
## 6. Get in touch with me
+ Email: mentalflow@ourdocs.cn
+ Blog: [The Tossed History of a Rookie Technician.](https://blog.ourdocs.cn)
+ ***And PRs and Issues are welcome, I'll try to improve features or fix bugs as soon as I can in my spare time, let's make RibbonUI better together, enjoy!***

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 46 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 70 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 193 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 34 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 154 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 102 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 26 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 38 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 41 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 199 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 28 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 35 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 502 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 518 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 669 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 550 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 630 KiB

View File

@ -0,0 +1,45 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple Computer//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleInfoDictionaryVersion</key>
<string>6.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>LSMinimumSystemVersion</key>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>NSHumanReadableCopyright</key>
<string>${MACOSX_BUNDLE_COPYRIGHT}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleDevelopmentRegion</key>
<string>${MACOSX_BUNDLE_DEVELOPMENT_REGION}</string>
<key>CFBundleAllowMixedLocalizations</key>
<true/>
<key>NSHighResolutionCapable</key>
<true/>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSSupportsAutomaticGraphicsSwitching</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,37 @@
#include <windows.h>
IDI_ICON1 ICON "${PROJECT_SOURCE_DIR}/resources/imgs/icon.ico"
VS_VERSION_INFO VERSIONINFO
FILEVERSION ${version_str}
PRODUCTVERSION ${version_str}
FILEFLAGSMASK 0x3fL
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_APP
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "Comments", "Made by Mentalflow."
VALUE "CompanyName", "${PROJECT_COMPANY}"
VALUE "FileDescription", "${PROJECT_BUNDLE_NAME}"
VALUE "FileVersion", "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
VALUE "InternalName", "${PROJECT_BUNDLE_NAME}.exe"
VALUE "LegalCopyright", "${PROJECT_COPYRIGHT}"
VALUE "OriginalFilename", "${PROJECT_BUNDLE_NAME}.exe"
VALUE "ProductName", "${PROJECT_BUNDLE_NAME}"
VALUE "ProductVersion", "${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}.${PROJECT_VERSION_PATCH}"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 1200
END
END

View File

@ -0,0 +1,134 @@
cmake_minimum_required(VERSION 3.21)
project(RibbonUIAPP VERSION ${PROJECT_VERSION} LANGUAGES CXX)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
list(APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/.cmake/)
if(QT_VERSION VERSION_GREATER_EQUAL "6.3")
qt_standard_project_setup()
qt_policy(SET QTP0001 NEW)
else()
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTORCC ON)
set(CMAKE_AUTOUIC ON)
endif()
if(APPLE)
set(CMAKE_OSX_ARCHITECTURES "arm64;x86_64" CACHE STRING "" FORCE)
endif()
string(TIMESTAMP TIME_YEAR %Y)
set(PROJECT_COMPANY "Mentalflow's Lab")
set(PROJECT_COPYRIGHT "Copyright (c) ${TIME_YEAR} Mentalflow's Lab. All rights reserved.")
set(PROJECT_DOMAIN "dev.ourdocs.cn.ribbonuiapp")
set(PROJECT_BUNDLE_NAME RibbonUI-APP)
set(version_str "${PROJECT_VERSION_MAJOR},${PROJECT_VERSION_MINOR},${PROJECT_VERSION_PATCH}")
add_definitions(-DRIBBONUIAPP_VERSION=${version_str})
find_package(Qt6 COMPONENTS Quick REQUIRED)
set(sources_files example.cpp)
if(CMAKE_BUILD_TYPE STREQUAL "Debug")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/app/debug)
else()
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/app/release)
endif()
if (WIN32)
configure_file(.cmake/win_app.rc.in ${CMAKE_BINARY_DIR}/win_app.rc)
set(app_icon_resource_windows ${CMAKE_BINARY_DIR}/win_app.rc)
qt_add_executable(${PROJECT_NAME}
${sources_files}
${app_icon_resource_windows}
)
file(TO_CMAKE_PATH "/" PATH_SEPARATOR)
if(MSVC)
set(DLLPATH ${CMAKE_SOURCE_DIR}/3rdparty/msvc/*.dll)
else()
set(DLLPATH ${CMAKE_SOURCE_DIR}/3rdparty/mingw/*.dll)
endif()
string(REPLACE "/" ${PATH_SEPARATOR} DLLPATH "${DLLPATH}")
file(GLOB DLL_FILES ${DLLPATH})
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy
${DLL_FILES}
${CMAKE_RUNTIME_OUTPUT_DIRECTORY}
)
elseif(APPLE)
set(MACOSX_BUNDLE_GUI_IDENTIFIER ${PROJECT_DOMAIN})
set(MACOSX_BUNDLE_BUNDLE_VERSION ${PROJECT_VERSION})
set(MACOSX_BUNDLE_SHORT_VERSION_STRING ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR})
set(MACOSX_BUNDLE_BUNDLE_NAME ${PROJECT_BUNDLE_NAME})
set(MACOSX_BUNDLE_COPYRIGHT ${PROJECT_COPYRIGHT})
set(MACOSX_BUNDLE_DEVELOPMENT_REGION "Chinese")
set(CMAKE_OSX_DEPLOYMENT_TARGET "11.0" CACHE STRING "Minimum OS X deployment version" FORCE)
set(MACOSX_BUNDLE_EXECUTABLE_NAME ${PROJECT_BUNDLE_NAME})
set(MACOSX_BUNDLE_ICON_FILE AppIcon)
configure_file(.cmake/MacOSXBundleInfo.plist.in ${CMAKE_BINARY_DIR}/Info.plist)
set(App_ICON "${PROJECT_SOURCE_DIR}/resources/imgs/AppIcon.icns")
set_source_files_properties(${App_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources")
qt_add_executable(${PROJECT_NAME}
${sources_files}
${App_ICON}
)
else()
qt_add_executable(${PROJECT_NAME}
${sources_files}
)
endif()
qt_add_qml_module(${PROJECT_NAME}
URI ${PROJECT_NAME}
VERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
RESOURCE_PREFIX "/qt/qml/"
QML_FILES example.qml about.qml components/RibbonMessageListViewExample.qml
RESOURCES resources/imgs/heart.png resources/imgs/search.png
)
set_target_properties(${PROJECT_NAME} PROPERTIES
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_BINARY_DIR}/Info.plist
MACOSX_BUNDLE TRUE
WIN32_EXECUTABLE TRUE
OUTPUT_NAME ${PROJECT_BUNDLE_NAME}
)
if(RIBBONUI_BUILD_STATIC_LIB)
target_link_libraries(${PROJECT_NAME} PRIVATE
RibbonUIplugin
)
target_compile_definitions(${PROJECT_NAME} PRIVATE
RIBBONUI_BUILD_STATIC_LIB
)
else()
if(WIN32)
if (MINGW)
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${RIBBONUI_QML_PLUGIN_DIRECTORY}/RibbonUI.dll"
${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
else()
if (NOT RIBBONUI_BUILD_STATIC_LIB)
if (CMAKE_BUILD_TYPE MATCHES "Debug")
set(DEBUG_POSTFIX "d")
endif()
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
COMMAND ${CMAKE_COMMAND} -E copy_if_different
"${RIBBONUI_QML_PLUGIN_DIRECTORY}/RibbonUI${DEBUG_POSTFIX}.dll"
${CMAKE_RUNTIME_OUTPUT_DIRECTORY})
endif()
endif()
endif()
endif()
target_link_libraries(${PROJECT_NAME} PRIVATE
Qt::Quick
RibbonUI
)
target_compile_definitions(${PROJECT_NAME} PRIVATE $<$<OR:$<CONFIG:Debug>,$<CONFIG:RelWithDebInfo>>:QT_QML_DEBUG>)
target_include_directories(${PROJECT_NAME} PUBLIC ${PROJECT_SOURCE_DIR})

View File

@ -0,0 +1,46 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import RibbonUI
RibbonWindow {
id: window
width: Math.max(content.width, content.height + title_bar.height, title_bar.minimumWidth) + content.anchors.margins * 2
minimumWidth: title_bar.minimumWidth
minimumHeight: content.height + title_bar.height + content.anchors.margins * 2
title: qsTr("About")
title_bar.show_darkmode_btn: false
title_bar.show_style_switch: false
windowStatus: RibbonWindow.Status.SingleInstance
ColumnLayout{
id: content
anchors{
centerIn: parent
margins: 10
}
spacing: 5
Image {
source: "qrc:/qt/qml/RibbonUI/resources/imgs/icon.png"
fillMode:Image.PreserveAspectFit
Layout.preferredHeight: 120
Layout.preferredWidth: height
Layout.alignment: Qt.AlignHCenter
layer.enabled: true
layer.effect: RibbonShadow{}
}
RibbonText{
Layout.alignment: Qt.AlignHCenter
text: "RibbonUI"
font.pixelSize: 16
}
RibbonText{
Layout.alignment: Qt.AlignHCenter
text: `©${new Date().getFullYear()} mentalfl0w`
}
RibbonText{
Layout.alignment: Qt.AlignHCenter
text: `Version: V${RibbonUI.version}`
}
}
}

View File

@ -0,0 +1,60 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import RibbonUI
Item {
id: root
implicitHeight: layout.height + layout.anchors.margins * 2
implicitWidth: 500
ColumnLayout{
id: layout
width: parent.width - anchors.margins * 2
anchors.centerIn: parent
anchors.margins: 30
RibbonText{
Layout.alignment: Qt.AlignHCenter
text: qsTr("Message List View Example")
font.pixelSize: 20
}
RibbonMessageListView{
id: view
auto_scroll_to_bottom: true
Layout.preferredHeight: 500
Layout.preferredWidth: parent.width
delegate: RibbonMessage{
id: msg
sender_text: `${model.time} ${model.recieved ? qsTr('Recieved') : qsTr('Sent')}`
RibbonText{
font.pixelSize: msg.font_size
color: RibbonTheme.dark_mode ? "white" : !model.recieved ? "white" : "black"
text: model.text ? model.text : ""
visible: model.text ? true : false
Layout.preferredWidth: implicitWidth < (view.width / 2 - padding) ? implicitWidth : (view.width / 2 - padding)
wrapMode: RibbonText.Wrap
}
}
}
RowLayout{
Layout.alignment: Qt.AlignHCenter
RibbonButton{
icon_source: RibbonIcons.AddCircle
text: qsTr('Add Message')
onClicked: {
view.message_model.append({
time: Qt.formatDateTime(new Date(), "yyyy-MM-dd hh:mm:ss.zzz"),
text: String(Math.random()*10),
recieved: (Math.floor(Math.random()*10))%2===0,
})
}
}
RibbonButton{
icon_source: RibbonIcons.DismissCircle
text: qsTr('Clear Message')
onClicked: {
view.message_model.clear()
}
}
}
}
}

View File

@ -0,0 +1,24 @@
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QtQml/qqmlextensionplugin.h>
#include "ribbonui.h"
#ifdef RIBBONUI_BUILD_STATIC_LIB
Q_IMPORT_QML_PLUGIN(RibbonUIPlugin)
#endif
int main(int argc, char *argv[])
{
RibbonUI::init(); // Must set before QGuiApplication
QGuiApplication app(argc, argv);
QQmlApplicationEngine engine;
RibbonUI::registerTypes(&engine);
const QUrl url(u"qrc:/qt/qml/RibbonUIAPP/example.qml"_qs);
QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
&app, [url](QObject *obj, const QUrl &objUrl) {
if (!obj && url == objUrl)
QCoreApplication::exit(-1);
}, Qt::QueuedConnection);
engine.load(url);
return app.exec();
}

View File

@ -0,0 +1,766 @@
import QtQuick
import QtQuick.Layouts
import QtQuick.Controls
import RibbonUI
RibbonWindow {
id:root
width: 1200
height: 800
title: qsTr("RibbonUI APP")
comfirmed_quit: true
property bool modern_style: RibbonTheme.modern_style
RibbonTabBar {
id: tab_bar
modern_style: root.modern_style
right_tool_bar: RowLayout{
spacing: 10
RibbonButton{
text:"Test Button 1"
icon_source: RibbonIcons.Alert
checkable: true
}
RibbonButton{
text:"Test Button 2"
}
}
RibbonTabPage{
id: basic_page
title: qsTr("Basic")
RibbonTabGroup{
width: slider_layout.width + 20
text: qsTr("Slider")
RowLayout{
id: slider_layout
anchors.centerIn: parent
height: parent.height
spacing: 0
RibbonSlider{
Layout.alignment: Qt.AlignVCenter
slide_width: 40
horizontal: false
value: 20
}
RibbonSlider{
Layout.alignment: Qt.AlignVCenter
slide_width: 40
horizontal: false
show_button: false
value: 40
}
ColumnLayout{
spacing: 0
Layout.alignment: Qt.AlignVCenter
RibbonSlider{
Layout.alignment: Qt.AlignHCenter
slide_width: 40
value: 60
}
RibbonSlider{
Layout.alignment: Qt.AlignHCenter
slide_width: 40
show_button: false
value: 80
}
}
}
}
RibbonTabGroup{
width: switch_layout.width + 30
text: qsTr("Switch Button")
RowLayout{
id: switch_layout
anchors.centerIn: parent
height: parent.height
spacing: 0
ColumnLayout{
spacing: 5
RibbonSwitchButton{
text: "Button"
grabber_checked_color: "red"
checked: true
}
RibbonSwitchButton{
text: "Button"
text_on_left: true
grabber_checked_color: "orange"
checked: true
}
RibbonSwitchButton{
grabber_checked_color: "blue"
}
}
ColumnLayout{
spacing: 5
RibbonSwitchButton{
text: "Button"
show_grabber_text: false
grabber_checked_color: "green"
}
RibbonSwitchButton{
text: "Button"
show_grabber_text: false
text_on_left: true
grabber_checked_color: "indigo"
checked: true
}
RibbonSwitchButton{
show_grabber_text: false
grabber_checked_color: "yellow"
checked: true
}
}
}
}
RibbonTabGroup{
width: checkbox_layout.width + 30
text: qsTr("CheckBox")
RowLayout{
id: checkbox_layout
anchors.centerIn: parent
height: parent.height
spacing: 0
ColumnLayout{
spacing: 10
RibbonCheckBox{
text: "CheckBox"
icon_filled_bg_color: "blue"
checked: true
}
RibbonCheckBox{
text: "CheckBox"
text_on_left: true
icon_filled_bg_color: "red"
}
RowLayout{
spacing: 30
RibbonCheckBox{
icon_filled_bg_color:"orange"
tip_text: "CheckBox"
show_tooltip: true
checked: true
}
RibbonCheckBox{
icon_filled_bg_color:"purple"
}
}
}
}
}
RibbonTabGroup{
width: button_layout.width + 30
text: qsTr("Button")
RowLayout{
id: button_layout
anchors.centerIn: parent
height: parent.height
spacing: 1
ColumnLayout{
spacing: 10
RibbonButton{
text:"Button"
icon_source: RibbonIcons.Accessibility
checkable: true
}
RibbonButton{
text:"Button"
}
RibbonButton{
text:"Button"
show_tooltip: false
}
}
ColumnLayout{
spacing: 10
RibbonButton{
text:"Button"
show_bg:false
icon_source: RibbonIcons.Beaker
checkable: true
}
RibbonButton{
text:"Button"
show_bg:false
}
RibbonButton{
text:"Button"
show_bg:false
show_tooltip: false
}
}
ColumnLayout{
spacing: 10
RibbonButton{
show_bg:false
icon_source: RibbonIcons.Badge
icon_source_filled: RibbonIcons_Filled.Badge
checkable: true
tip_text: "Button"
}
RibbonButton{
show_bg:false
icon_source: RibbonIcons.Clock
icon_source_filled: RibbonIcons_Filled.Clock
tip_text: "Button"
}
RibbonButton{
show_bg:false
icon_source: RibbonIcons.Board
icon_source_filled: RibbonIcons_Filled.Board
checkable: true
tip_text: "Button"
show_tooltip: false
}
}
}
}
RibbonTabGroup{
width: pushbutton_layout.width + 30
text: qsTr("Push Button")
RowLayout{
id: pushbutton_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
RibbonPushButton{
text: qsTr("No Menu")
icon_source: RibbonIcons.AttachText
}
RibbonPushButton{
text: qsTr("Menu")
icon_source: RibbonIcons.MeetNow
Action{
text: "Test Item 1"
}
RibbonMenuSeparator{}
Action{
text: "Test Item 2"
enabled: false
}
}
RibbonPushButton{
text: qsTr("No Menu")
icon_source: "qrc:/qt/qml/RibbonUIAPP/resources/imgs/heart.png"
icon_size: height-5
}
RibbonPushButton{
text: qsTr("Menu")
icon_source: "qrc:/qt/qml/RibbonUIAPP/resources/imgs/search.png"
Action{
text: "Test Item 3"
}
RibbonMenuSeparator{}
Action{
text: "Test Item 4"
enabled: false
}
}
}
}
}
RibbonTabPage{
title: qsTr("Input")
RibbonTabGroup{
width: lineedit_layout.width + 30
text: qsTr("Line Edit")
RowLayout{
id: lineedit_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
ColumnLayout{
spacing: 10
Layout.fillHeight: true
RibbonLineEdit{
}
RibbonLineEdit{
show_clear_btn:false
}
}
ColumnLayout{
spacing: 10
Layout.fillHeight: true
RibbonLineEdit{
icon_source:RibbonIcons.Search
}
RibbonLineEdit{
icon_source:RibbonIcons.Keyboard
show_clear_btn:false
}
}
}
}
RibbonTabGroup{
width: lineedit_layout.width + 30
text: qsTr("Text Edit")
RowLayout{
id: textedit_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
ColumnLayout{
spacing: 30
Layout.fillHeight: true
RibbonTextEdit{
max_height: 50
}
RibbonTextEdit{
max_height: 30
show_clear_btn:false
}
}
ColumnLayout{
spacing: 30
Layout.fillHeight: true
RibbonTextEdit{
max_height: 50
icon_source:RibbonIcons.Search
}
RibbonTextEdit{
max_height: 30
icon_source:RibbonIcons.Keyboard
show_clear_btn:false
}
}
}
}
RibbonTabGroup{
width: combobox_layout.width + 30
text: qsTr("Combo Box")
RowLayout{
id: combobox_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
ColumnLayout{
spacing: 10
Layout.fillHeight: true
RibbonComboBox{
model: ListModel {
ListElement { text: "Test Item 1" }
ListElement { text: "Test Item 2" }
ListElement { text: "Test Item 3" }
}
}
RibbonComboBox{
editable: true
model: ListModel {
id: model
ListElement { text: "Test Item 1" }
ListElement { text: "Test Item 2" }
ListElement { text: "Test Item 3" }
}
onAccepted: {
if (find(editText) === -1 && editText)
model.append({text: editText})
}
}
}
ColumnLayout{
spacing: 10
Layout.fillHeight: true
RibbonComboBox{
model: ListModel {
ListElement { text: "Test Item 1" }
ListElement { text: "Test Item 2" }
ListElement { text: "Test Item 3" }
}
icon_source: RibbonIcons.Beaker
}
RibbonComboBox{
editable: true
model: ListModel {
id: model_1
ListElement { text: "Test Item 1" }
ListElement { text: "Test Item 2" }
ListElement { text: "Test Item 3" }
}
icon_source: RibbonIcons.Calendar
onAccepted: {
if (find(editText) === -1 && editText)
model_1.append({text: editText})
}
}
}
}
}
RibbonTabGroup{
width: spinbox_layout.width + 30
text: qsTr("Spin Box")
show_border: false
RowLayout{
id: spinbox_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
ColumnLayout{
spacing: 10
Layout.fillHeight: true
RibbonSpinBox{
width: 100
}
RibbonSpinBox{
id: spinbox
width: 80
icon_source: RibbonIcons.DataPie
validator: DoubleValidator {
bottom: Math.min(spinbox.from, spinbox.to)
top: Math.max(spinbox.from, spinbox.to)
decimals: 2
notation: DoubleValidator.StandardNotation
}
textFromValue: function(value, locale) {
return Number(value / 100).toLocaleString(locale, 'f', 2)
}
valueFromText: function(text, locale) {
return Math.round(Number.fromLocaleString(locale, text) * 100)
}
}
}
}
}
}
RibbonTabPage{
title: qsTr("Views")
RibbonTabGroup{
width: message_list_view_layout.width + 30
text: qsTr("MessageListView")
RowLayout{
id: message_list_view_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
RibbonButton{
text: qsTr('Open Message List View')
icon_source: RibbonIcons.Open
onClicked: {
Window.window.popup.show_content("qrc:/qt/qml/RibbonUIAPP/components/RibbonMessageListViewExample.qml")
}
}
}
}
}
RibbonTabPage{
title: qsTr("Others")
RibbonTabGroup{
width: text_layout.width + 30
text: qsTr("Text")
RowLayout{
id: text_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
ColumnLayout{
spacing: 10
Layout.fillHeight: true
RibbonText{
font.pixelSize: 13
text: "Test Text"
}
RibbonText{
font.pixelSize: 13
text: "Test Text (Read Only)"
view_only: true
}
}
}
}
RibbonTabGroup{
width: menu_layout.width + 30
text: qsTr("Menu")
RowLayout{
id: menu_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
RibbonButton{
text: qsTr("Open Menu")
icon_source: RibbonIcons.Open
onClicked: menu.popup()
}
}
}
RibbonTabGroup{
width: popup_layout.width + 30
text: qsTr("Popup")
RowLayout{
id: popup_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
ColumnLayout{
spacing: 10
Layout.fillHeight: true
RibbonButton{
text: qsTr("Open Popup")
icon_source: RibbonIcons.Open
onClicked: popup.open()
}
RibbonButton{
text: qsTr("Open Popup Dialog (Double Choices)")
icon_source: RibbonIcons.Open
onClicked: {
dialog.buttonFlags = RibbonPopupDialogType.NegativeButton | RibbonPopupDialogType.PositiveButton
dialog.open()
}
}
RibbonButton{
text: qsTr("Open Popup Dialog (Triple Choices)")
icon_source: RibbonIcons.Open
onClicked: {
dialog.buttonFlags = RibbonPopupDialogType.NegativeButton | RibbonPopupDialogType.PositiveButton | RibbonPopupDialogType.NeutralButton
dialog.open()
}
}
}
RibbonPopup{
id: popup
height: 200
width: height
target: window_items
blur_enabled: true
target_rect: Qt.rect(window_items.x + x, window_items.y + y, width, height)
}
RibbonPopupDialog{
id: dialog
target: window_items
blur_enabled: true
target_rect: Qt.rect(window_items.x + x, window_items.y + y, width, height)
}
}
}
RibbonTabGroup{
width: theme_layout.width + 30
text: qsTr("Theme")
RowLayout{
id: theme_layout
anchors.centerIn: parent
height: parent.height
spacing: 10
RibbonComboBox{
id: theme_combo
model: ListModel {
id: model_theme
ListElement { text: "Light" }
ListElement { text: "Dark" }
ListElement { text: "System" }
}
icon_source: RibbonIcons.DarkTheme
Component.onCompleted: update_state()
onActivated: {
if (currentText === "System")
RibbonTheme.theme_mode = RibbonThemeType.System
else if (currentText === "Light")
RibbonTheme.theme_mode = RibbonThemeType.Light
else
RibbonTheme.theme_mode = RibbonThemeType.Dark
}
Connections{
target: RibbonTheme
function onTheme_modeChanged(){
theme_combo.update_state()
}
}
function update_state(){
let str = (RibbonTheme.theme_mode === RibbonThemeType.System ? "System" : RibbonTheme.theme_mode === RibbonThemeType.Light ? "Light" : "Dark")
currentIndex = find(str)
}
}
}
}
}
}
RibbonPaperView{
id: view
anchors.fill: parent
top_padding: tab_bar.height
bottom_padding: bottom_bar.height
page_width: (page_slider.value / 100.0) * width
spacing: 0
ColumnLayout{
Layout.alignment: Qt.AlignCenter
Layout.topMargin: 30
spacing: 20
RibbonText{
Layout.alignment: Qt.AlignHCenter
Layout.topMargin: 50
text: "RibbonUI"
font{
pixelSize: 50
bold: true
italic: true
}
}
Image {
source: "qrc:/qt/qml/RibbonUI/resources/imgs/icon.png"
fillMode:Image.PreserveAspectFit
Layout.preferredHeight: 300
Layout.preferredWidth: height
Layout.alignment: Qt.AlignHCenter
layer.enabled: true
layer.effect: RibbonShadow{}
}
RibbonText{
Layout.alignment: Qt.AlignHCenter
text: "A Lightweight, minimalist and \nelegant Qt component library."
font{
pixelSize: 30
bold: true
italic: true
}
}
RibbonText{
Layout.alignment: Qt.AlignHCenter
text: "Author: mentalfl0w"
font{
pixelSize: 25
bold: true
italic: true
}
}
RibbonText{
Layout.alignment: Qt.AlignHCenter
text: "Email: mentalflow@ourdocs.cn"
font{
pixelSize: 25
bold: true
italic: true
}
}
RibbonText{
Layout.alignment: Qt.AlignHCenter
text: `Current Version: V${RibbonUI.version}`
font{
pixelSize: 20
bold: true
italic: true
}
}
}
}
RibbonBottomBar{
id: bottom_bar
anchors{
left: parent.left
right: parent.right
bottom: parent.bottom
}
right_content: RowLayout{
clip: true
spacing:1
Layout.preferredHeight: parent.height
layoutDirection: Qt.RightToLeft
RibbonSlider{
id: page_slider
slide_width: 80
show_filled_color: false
value: 70
}
RibbonButton{
text:"Test Button 3"
show_bg:false
adapt_height:true
icon_source: RibbonIcons.Airplane
}
RibbonButton{
text:"Test Button 4"
show_bg:false
adapt_height:true
}
}
RibbonButton{
text:"Test Button 5"
show_bg:false
adapt_height:true
icon_source: RibbonIcons.AccessTime
checkable: true
}
RibbonButton{
text:"Test Button 6"
show_bg:false
adapt_height:true
}
RibbonButton{
show_bg:false
adapt_height:true
icon_source: RibbonIcons.AppStore
checkable: true
tip_text: "Test Button 7"
}
}
title_bar.right_content:RowLayout{
spacing: 1
layoutDirection: Qt.RightToLeft
RibbonButton{
show_bg:false
icon_source: RibbonIcons.Info
icon_source_filled: RibbonIcons_Filled.Info
tip_text: qsTr("About")
hover_color: Qt.rgba(0,0,0, 0.3)
pressed_color: Qt.rgba(0,0,0, 0.4)
text_color: title_bar.title_text_color
text_color_reverse: false
onClicked: root.show_window(Qt.resolvedUrl("about.qml"))
}
RibbonButton{
show_bg:false
icon_source: RibbonIcons.CalendarStar
icon_source_filled: RibbonIcons_Filled.CalendarStar
checkable: true
tip_text: "Test Button 11"
hover_color: Qt.rgba(0,0,0, 0.3)
pressed_color: Qt.rgba(0,0,0, 0.4)
text_color: title_bar.title_text_color
text_color_reverse: false
}
}
title_bar.left_content:RowLayout{
spacing: 1
RibbonButton{
show_bg:false
icon_source: RibbonIcons.ChevronDown
tip_text: "Test Button 8"
hover_color: Qt.rgba(0,0,0, 0.3)
pressed_color: Qt.rgba(0,0,0, 0.4)
text_color: title_bar.title_text_color
text_color_reverse: false
RibbonMenu{
id:menu
width: 200
Action{
text: "Test Long Text Test Long Text Test Long Text"
checkable: true
}
RibbonMenuSeparator{}
Action{
text: "Test Item 1"
enabled: false
}
RibbonMenu{
width: parent.width
title: "Sub Menu"
Action { text: qsTr("Test Item 2") }
Action { text: qsTr("Test Item 3") }
}
}
onClicked:menu.popup()
}
RibbonButton{
show_bg:false
icon_source: RibbonIcons.Apps
icon_source_filled: RibbonIcons_Filled.Apps
checkable: true
tip_text: "Test Button 9"
hover_color: Qt.rgba(0,0,0, 0.3)
pressed_color: Qt.rgba(0,0,0, 0.4)
text_color: title_bar.title_text_color
text_color_reverse: false
enabled: false
}
}
}

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 13 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 126 KiB

View File

@ -0,0 +1,130 @@
# References:
# https://clang.llvm.org/docs/ClangFormatStyleOptions.html
# https://code.qt.io/cgit/qt/qt5.git/tree/_clang-format
BasedOnStyle: LLVM
Standard: c++17
# 指针和引用的对齐方式。
# 可能的值有:
# PAS_Left (在配置中: Left) 指针左对齐。
# PAS_Right (在配置中: Right) 指针右对齐。
# PAS_Middle (在配置中: Middle) 指针中间对齐。
PointerAlignment: Right
# public/protected/private 等访问修饰符偏移量
AccessModifierOffset: -4
# 缩进长度
IndentWidth: 4
# 连续空行的最大数
MaxEmptyLinesToKeep: 999
# 在OC中的@property后面添加一个空格。例如使用“@property (readonly)”而不是“@property(readonly)”
ObjCSpaceAfterProperty: true
# OC块中所拍的字符数
ObjCBlockIndentWidth: 4
# 取决于值, 语句“int f() { return 0; }”可以被放到一个单行。
# 可能的值有:
# SFS_None (在配置中: None) 从不合并方法或函数到单独的一行。
# SFS_Empty (在配置中: Empty) 仅合并空的函数。
# SFS_Inline (在配置中: Inline) 仅合并类中定义的方法或函数. 意味着 “empty”.
# SFS_All (在配置中: All) 合并所有的方法适应单行.
AllowShortFunctionsOnASingleLine: None
# 如果为真true, 语句“if (a) return;” 能被放到单行。
AllowShortIfStatementsOnASingleLine: false
# 如果为真true, 对齐注释。
AlignTrailingComments: true
# 如果为真,对齐连续的宏定义
AlignConsecutiveMacros: true
# 如果为真true,将会在“[”之后和“]”之前插入空格。
SpacesInSquareBrackets: false
# 如果为真true, 将会在“(”之后和“)”之前插入空格。
SpacesInParentheses : false
# 如果为真true, 校准连续的声明。
# 这将会校准连续多行的声明的名字。这将会导致像下面这样的格式:
# int aaaa = 12;
# float b = 23;
# std::string ccc = 23;
AlignConsecutiveDeclarations: false
# 如果为真true连续调整多行
# 这将会调整连续行中的分配操作符。这将会导致像下面这样的格式:
# int aaaa = 12;
# int b = 23;
# int ccc = 23;
AlignConsecutiveAssignments: false
# 如果为假false移除分配操作符=)前空格。
SpaceBeforeAssignmentOperators: true
# 如果为真true, 将会在字面量容器中插入空格(例如 OC和Javascript的数组和字典字面量)。
SpacesInContainerLiterals: false
# 缩进case标签
IndentCaseLabels: true
# 如果表达式中包含函数调用,并且函数调用因为表达式太长被放到了下一行,是否缩进
IndentWrappedFunctionNames: true
# 如果为真true, 保持块的起始空行。
# true: false:
# if (foo) { vs. if (foo) {
# bar();
# bar(); }
# }
KeepEmptyLinesAtTheStartOfBlocks: true
# 允许所有参数都被放在下一行
AllowAllParametersOfDeclarationOnNextLine: false
# 使用C风格强制类型转换后是否在中间添加一个空格
SpaceAfterCStyleCast: true
# 在模板定义后换行
AlwaysBreakTemplateDeclarations: Yes
# Tab长度
TabWidth: 4
# 是否使用Tab
UseTab: Never
# 在括号后对齐参数
# someLongFunction(argument1,
# argument2);
AlignAfterOpenBracket: Align
# 名字空间内部缩进
NamespaceIndentation: All
# 一行最长列数
ColumnLimit: 100
# 按层次缩进宏定义
IndentPPDirectives: AfterHash
# 预处理语句缩进为 2
PPIndentWidth: 2
# 数组元素对齐
AlignArrayOfStructures: Left
# 不对头文件排序
SortIncludes: Never
FixNamespaceComments: false
StatementMacros: ['__qas_attr__', '__qas_exclude__', '__qas_include__']
ForEachMacros: [ foreach, Q_FOREACH, BOOST_FOREACH, forever, Q_FOREVER, QBENCHMARK, QBENCHMARK_ONCE ]

View File

@ -0,0 +1,98 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.log
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
# *.res
# *.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
__pycache__
*.pyc
# Binaries
# --------
*.dll
*.exe
.DS_Store
*/.DS_Store
build-*
build/
cmake-build*
*.user
*.lnk
_workingDir*
.vscode
.idea
.cache
cache
.vs
out/
CMakeSettings.json
# /vcpkg
/data
/*.natvis
*.sublime-*
setup-vcpkg.json
setup-vcpkg-temp*

View File

@ -0,0 +1,4 @@
[submodule "qmsetup"]
path = qmsetup
url = ../../stdware/qmsetup.git
branch = main

View File

@ -0,0 +1,84 @@
cmake_minimum_required(VERSION 3.19)
project(QWindowKit VERSION 1.1.1.0 LANGUAGES CXX)
# ----------------------------------
# Build Options
# ----------------------------------
option(QWINDOWKIT_BUILD_STATIC "Build static libraries" OFF)
option(QWINDOWKIT_BUILD_WIDGETS "Build widgets module" ON)
option(QWINDOWKIT_BUILD_QUICK "Build quick module" OFF)
option(QWINDOWKIT_BUILD_EXAMPLES "Build examples" OFF)
option(QWINDOWKIT_BUILD_DOCUMENTATIONS "Build documentations" OFF)
option(QWINDOWKIT_INSTALL "Install library" ON)
option(QWINDOWKIT_ENABLE_QT_WINDOW_CONTEXT "Enable Qt Window Context anyway" OFF)
option(QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS "Enable system borders on Windows" ON)
option(QWINDOWKIT_ENABLE_STYLE_AGENT "Enable building style agent" ON)
# ----------------------------------
# CMake Settings
# ----------------------------------
if(MSVC)
set(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS} /manifest:no")
set(CMAKE_MODULE_LINKER_FLAGS "${CMAKE_MODULE_LINKER_FLAGS} /manifest:no")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} /manifest:no")
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /utf-8")
if(NOT DEFINED CMAKE_DEBUG_POSTFIX)
set(CMAKE_DEBUG_POSTFIX "d")
endif()
elseif(MINGW)
set(CMAKE_STATIC_LIBRARY_PREFIX "")
set(CMAKE_SHARED_LIBRARY_PREFIX "")
endif()
if(QWINDOWKIT_INSTALL)
include(GNUInstallDirs)
include(CMakePackageConfigHelpers)
endif()
# ----------------------------------
# Project Variables
# ----------------------------------
set(QWINDOWKIT_VERSION ${PROJECT_VERSION})
set(QWINDOWKIT_INSTALL_NAME ${PROJECT_NAME})
# ----------------------------------
# Find basic dependencies
# ----------------------------------
find_package(qmsetup QUIET)
if(NOT TARGET qmsetup::library)
# Modify this variable according to your project structure
set(_source_dir ${CMAKE_CURRENT_SOURCE_DIR}/qmsetup)
# Import install function
include("${_source_dir}/cmake/modules/InstallPackage.cmake")
# Install package in place
set(_package_path)
qm_install_package(qmsetup
SOURCE_DIR ${_source_dir}
BUILD_TYPE Release
RESULT_PATH _package_path
)
# Find package again
find_package(qmsetup REQUIRED PATHS ${_package_path})
# Update import path
set(qmsetup_DIR ${_package_path} CACHE PATH "" FORCE)
endif()
qm_import(Filesystem)
qm_init_directories()
# ----------------------------------
# Add source modules
# ----------------------------------
add_subdirectory(src)
if(QWINDOWKIT_BUILD_EXAMPLES)
add_subdirectory(examples)
endif()

View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -0,0 +1,301 @@
# QWindowKit
Cross-platform window customization framework for Qt Widgets and Qt Quick.
This project inherited most of [wangwenx190 FramelessHelper](https://github.com/wangwenx190/framelesshelper)
implementation, with a complete refactoring and upgrading of the architecture.
Feature requests are welcome.
## Join with Us :triangular_flag_on_post:
You can join our [Discord channel](https://discord.gg/grrM4Tmesy). You can share your findings, thoughts and ideas on improving / implementing FramelessHelper functionalities on more platforms and apps!
## Supported Platforms
+ Microsoft Windows
+ Apple macOS (11+)
+ GNU/Linux
## Features
+ Full support of Windows 11 Snap Layout
+ Better workaround to handle Windows 10 top border issue
+ Support Mac system buttons geometry customization
+ Simpler APIs, more detailed documentations and comments
## Gallery
### Windows 11 (With Snap Layout)
![image](./docs/images/win11.png)
### Windows 10 (And 7, Vista)
![image](./docs/images/win10.png)
### macOS & Linux
| macOS | Linux (Ubuntu 20.04) |
|:-------------------------------:|:---------------------------------:|
| ![image](./docs/images/mac.png) | ![image](./docs/images/linux.png) |
## Requirements
| Component | Requirement | Details |
|:---------:|:-----------:|:-------------------------:|
| Qt | \>=5.12 | Core, Gui, Widgets, Quick |
| Compiler | \>=C++17 | MSVC 2019, GCC, Clang |
| CMake | \>=3.19 | >=3.20 is recommended |
### Tested Compilers
+ Windows
+ MSVC: 2019, 2022
+ MinGW (GCC): 13.2.0
+ macOS
+ Clang 14.0.3
+ Ubuntu
+ GCC: 9.4.0
## Dependencies
+ Qt 5.12 or higher
+ [qmsetup](https://github.com/stdware/qmsetup)
## Integrate
### Configure Options
+ `QWINDOWKIT_BUILD_DOCUMENTATIONS`
+ If you have installed `Doxygen`, you can **enable** this option so that the documentations will also be built and installed.
+ If not, you can read the comments in *qdoc* style in `cpp` files to get detailed usages of the public APIs.
+ `QWINDOWKIT_ENABLE_WINDOWS_SYSTEM_BORDERS`
+ If you don't want the system borders on Windows 10/11, you can **disable** this option.
+ If so, the Windows 10 top border issue will disappear. However, part of the client edge area will be occupied as the resizing margins.
+ `QWINDOWKIT_ENABLE_QT_WINDOW_CONTEXT`
+ If you want to use pure Qt emulated frameless implementation, you can **enable** this option.
+ If so, all system native features will be lost.
+ `QWINDOWKIT_ENABLE_STYLE_AGENT`
+ Select whether to exclude the style component by **disabling** this option according to your requirements and your Qt version.
### Build & Install
```sh
git clone --recursive https://github.com/stdware/qwindowkit
cd qwindowkit
cmake -B build -S . \
-Dqmsetup_DIR=<dir> \ # Optional
-DCMAKE_INSTALL_PREFIX=/path/install \
-G "Ninja Multi-Config"
cmake --build build --target install --config Debug
cmake --build build --target install --config Release
```
You can also include this directory as a subproject if you choose CMake as your build system.
For other build systems, you need to install with CMake first and include the corresponding configuration files in your
project.
### Import
#### CMake Project
```sh
cmake -B build -DQWindowKit_DIR=/path/install/lib/cmake/QWindowKit
```
```cmake
find_package(QWindowKit REQUIRED)
target_link_libraries(widgets_app PUBLIC QWindowKit::Widgets)
target_link_libraries(quick_app PUBLIC QWindowKit::Quick)
```
#### QMake Project
```cmake
# WidgetsApp.pro
include("/path/install/share/QWindowKit/qmake/QWKWidgets.pri")
# QuickApp.pro
include("/path/install/share/QWindowKit/qmake/QWKQuick.pri")
```
#### Visual Studio Project
TODO
## Quick Start
### Qt Widgets Application
#### Initialization
The following initialization should be done before any widget constructs.
```cpp
#include <QtWidgets/QApplication>
int main(int argc, char *argv[])
{
QGuiApplication::setAttribute(Qt::AA_DontCreateNativeWidgetSiblings)
// ...
}
```
#### Setup Window Agent
First, setup `WidgetWindowAgent` for your top `QWidget` instance. (Each window needs its own agent.)
```c++
#include <QWKWidgets/widgetwindowagent.h>
MyWidget::MyWidget(QWidget *parent) {
// ...
auto agent = new QWK::WidgetWindowAgent(this);
agent->setup(this);
// ...
}
```
If you don't want to derive a new widget class or change the constructor, you can initialize the agent after the window
constructs.
```c++
auto w = new MyWidget();
auto agent = new QWK::WidgetWindowAgent(w);
agent->setup(w);
```
#### Construct Title bar
Then, construct your title bar widget, without which the window lacks the basic interaction feature, and it's better to
put it into the window's layout.
You can use the [`WindowBar`](examples/shared/widgetframe/windowbar.h) provided by `WidgetFrame` in the examples as the
container of your title bar components.
Let `WidgetWindowAgent` know which widget the title bar is.
```c++
agent->setTitleBar(myTitleBar);
```
Next, set system button hints to let `WidgetWindowAgent` know the role of the child widgets, which is important for the
Snap Layout to work.
```c++
agent->setSystemButton(QWK::WindowAgentBase::WindowIcon, myTitleBar->iconButton());
agent->setSystemButton(QWK::WindowAgentBase::Minimize, myTitleBar->minButton());
agent->setSystemButton(QWK::WindowAgentBase::Maximize, myTitleBar->maxButton());
agent->setSystemButton(QWK::WindowAgentBase::Close, myTitleBar->closeButton());
```
Doing this does not mean that these buttons' click events are automatically associated with window actions, you still need to manually connect the signals and slots to emulate the native window behaviors.
On macOS, this step can be skipped because it is better to use the buttons provided by the system.
Last but not least, set hit-test visible hint to let `WidgetWindowAgent` know other widgets that desire to receive mouse events.
```c++
agent->setHitTestVisible(myTitleBar->menuBar(), true);
```
The rest region within the title bar will be regarded as the draggable area for the user to move the window.
<!-- #### Window Attributes (Experimental)
On Windows 11, you can use this API to enable system effects.
```c++
agent->setWindowAttribute("mica", true);
```
Available keys: `mica`, `mica-alt`, `acrylic`, `dark-mode`. -->
### Qt Quick Application
#### Initialization
Make sure you have registered `QWindowKit` into QtQuick:
```cpp
#include <QWKQuick/qwkquickglobal.h>
int main(int argc, char *argv[])
{
// ...
QQmlApplicationEngine engine;
// ...
QWK::registerTypes(&engine);
// ...
}
```
#### Setup Window Components
Then you can use `QWindowKit` data types and classes by importing it's URI:
```qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QWindowKit 1.0
Window {
id: window
visible: false // We hide it first, so we can move the window to our desired position silently.
Component.onCompleted: {
windowAgent.setup(window)
window.visible = true
}
WindowAgent {
id: windowAgent
// ...
}
}
```
You can omit the version number or use "auto" instead of "1.0" for the module URI if you are using Qt6.
### Learn More
See [examples](examples) for more demo use cases. The examples have no High DPI support.
+ QWindowKit Internals [TODO]
+ [FramelessHelper Related](docs/framelesshelper-related.md)
### Vulnerabilities
+ Once you have made the window frameless, it will not be able to switch back to the system border.
+ There must not be any internal child widget with `Qt::WA_NativeWindow` property enabled, otherwise the native features and display may be abnormal. Therefore, do not set any widget that has called `QWidget::winId()` or `QWidget::setAttribute(Qt::WA_NativeWindow)` as a descendant of a frameless window.
+ If you really need to move widgets between different windows, make sure that the widget is not a top-level window and wrap it with a frameless container.
## TODO
+ Fix 5.15 window abnormal behavior
+ More documentations
+ When do we support Linux native features?
## Special Thanks
+ [Maplespe](https://github.com/Maplespe)
+ [zhiyiYo](https://github.com/zhiyiYo)
## License
QWindowKit is licensed under the [Apache 2.0 License](./LICENSE).
<!--
**You MUST keep a copyright notice of QWindowKit in a prominent place on your project, such as the README document and the About Dialog.**
**You MUST NOT remove the license text from the header files and source files of QWindowKit.**
-->

View File

@ -0,0 +1,89 @@
# FramelessHelper 2.x
Cross-platform window customization framework for Qt Widgets and Qt Quick. Supports Windows, Linux and macOS.
## Join with Us :triangular_flag_on_post:
You can join our [Discord channel](https://discord.gg/grrM4Tmesy) to communicate with us. You can share your findings, thoughts and ideas on improving / implementing FramelessHelper functionalities on more platforms and apps!
## More
### Title Bar Design Guidance
- Microsoft: <https://docs.microsoft.com/en-us/windows/apps/design/basics/titlebar-design>
- KDE: <https://develop.kde.org/hig/>
- GNOME: <https://developer.gnome.org/hig/patterns/containers/header-bars.html>
- Apple: <https://developer.apple.com/design/human-interface-guidelines/macos/windows-and-views/window-anatomy/>
## Platform Notes
### Windows
- If DWM composition is disabled in some very rare cases (only possible on Windows 7), the top-left corner and top-right corner will appear in round shape. The round corners can be restored to square if you re-enable DWM composition.
- There's an OpenGL driver bug which will cause some frameless windows have a strange black bar right on top of your homemade title bar, and it also makes the controls in your windows shifted to the bottom-right corner for some pixels. It's a bug of your graphics card driver, specifically, your OpenGL driver, not FramelessHelper. There are some solutions provided by our users but some of them may not work in all conditions, you can pick one from them:
Solution | Principle
-------- | ---------
Upgrade the graphics driver | Try to use a newer driver which may ship with the fix
Change the system theme to "Basic" (in contrary to "Windows Aero") | Let Windows use pure software rendering
If there are multiple graphics cards, use another one instead | Try to use a different driver which may don't have such bug at all
Upgrade the system to at least Windows 11 | Windows 11 redesigned the windowing system so the bug can no longer be triggered
Remove the `WS_THICKFRAME` and `WS_OVERLAPPED` styles from the window, and maybe also add the `WS_POPUP` style at the same time, and don't do anything inside the `WM_NCCALCSIZE` block (just return `false` directly or remove/comment out the whole block) | Try to mirror Qt's `FramelessWindowHint`'s behavior
Use `Qt::FramelessWindowHint` instead of doing the `WM_NCCALCSIZE` trick | Qt's rendering code path is totally different between these two solutions
Force Qt to use the ANGLE backend instead of the Desktop OpenGL | ANGLE will translate OpenGL directives into D3D ones
Force Qt to use pure software rendering instead of rendering through OpenGL | Qt is not using OpenGL at all
Force Qt to use the Mesa 3D libraries instead of normal OpenGL | Try to use a different OpenGL implementation
Use Direct3D/Vulkan/Metal instead of OpenGL | Just don't use the buggy OpenGL
If you are lucky enough, one of them may fix the issue for you. If not, you may try to use multiple solutions together. **But I can't guarantee the issue can 100% be fixed.**
- Due to there are many sub-versions of Windows 10, it's highly recommended to use the latest version of Windows 10, at least **no older than Windows 10 1809**. If you try to use this framework on some very old Windows 10 versions such as 1507 or 1607, there may be some compatibility issues. Using this framework on Windows 7 is also supported but not recommended. To get the most stable behavior and the best appearance, you should use it on the latest version of Windows 10 or Windows 11.
- To make the snap layout work as expected, there are some additional rules for your homemade system buttons to follow:
- **Add a manifest file to your application. In the manifest file, you need to claim your application supports Windows 11 explicitly. This step is VERY VERY IMPORTANT. Without this step, the snap layout feature can't be enabled.**
- Call `setSystemButton()` for each button (it can be any *QWidget* or *QQuickItem*) to let FramelessHelper know which is the minimize/maximize/close button.
### Linux
- FramelessHelper will force your application to use the _XCB_ platform plugin when running on Wayland.
- The resize area is inside of the window.
### macOS
- Some users reported that the window is not resizable on some old macOS versions.
## Special Thanks
*Ordered by first contribution time (it may not be very accurate, sorry)*
- [Yuhang Zhao](https://github.com/wangwenx190): Help me create this project. This project is mainly based on his code.
- [Julien](https://github.com/JulienMaille): Help me test this library on many various environments and help me fix the bugs we found. Contributed many code to improve this library. The MainWindow example is mostly based on his code.
- [Altair Wei](https://github.com/altairwei): Help me fix quite some small bugs and give me many important suggestions, the 2.x version is also inspired by his idea during our discussions.
- [Kenji Mouri](https://github.com/MouriNaruto): Give me a lot of help on Win32 native developing.
- [Dylan Liu](https://github.com/mentalfl0w): Help me improve the build process on macOS.
- [SineStriker](https://github.com/SineStriker): Spent over a whole week helping me improve the Snap Layout implementation, fixing potential bugs and also give me a lot of professional and useful suggestions. Without his great effort, the new implementation may never come.
- And also thanks to other contributors not listed here! Without their valuable help, this library wouldn't have such good quality and user experience!
## License
```text
MIT License
Copyright (C) 2021-2023 by wangwenx190 (Yuhang Zhao)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.
```

Binary file not shown.

After

Width:  |  Height:  |  Size: 53 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 37 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 349 KiB

View File

@ -0,0 +1,23 @@
set(QWK_EXAMPLES_DIR ${CMAKE_CURRENT_SOURCE_DIR})
macro(qwk_add_example _target)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
add_executable(${_target})
qm_configure_target(${_target} ${ARGN})
qm_add_win_rc(${_target} ICON ${QWK_EXAMPLES_DIR}/shared/resources/app/example.ico)
qm_add_win_manifest(${_target})
qm_add_mac_bundle(${_target} ICON ${QWK_EXAMPLES_DIR}/shared/resources/app/example.icns)
endmacro()
add_subdirectory(shared)
if(QWINDOWKIT_BUILD_WIDGETS)
add_subdirectory(mainwindow)
endif()
if(QWINDOWKIT_BUILD_QUICK)
add_subdirectory(qml)
endif()

View File

@ -0,0 +1,9 @@
project(QWKExample_MainWindow)
file(GLOB _src *.h *.cpp)
qwk_add_example(${PROJECT_NAME}
SOURCES ${_src} mainwindow.qrc ../shared/resources/shared.qrc
QT_LINKS Core Gui Widgets # MultimediaWidgets
LINKS QWKWidgets WidgetFrame
)

View File

@ -0,0 +1,158 @@
/* 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;
min-height: 28px;
}
/* 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(255, 255, 255, 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(255, 255, 255, 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(":/app/example.png");
qproperty-iconSize: 18px 18px;
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: 1px 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;
}
MainWindow[custom-style=true] {
background-color: transparent;
}
QWidget#clock-widget {
font-size: 75px;
color: #FEFEFE;
font-weight: bold;
background-color: transparent;
}

View File

@ -0,0 +1,156 @@
/* Window bar */
QWK--WindowBar[bar-active=true] {
background-color: #195ABE;
}
QWK--WindowBar[bar-active=false] {
background-color: #195ABE;
}
/* Title label */
QWK--WindowBar>QLabel#win-title-label {
padding: 0;
border: none;
color: #ECECEC;
background-color: transparent;
min-height: 28px;
}
/* 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(":/app/example.png");
qproperty-iconSize: 18px 18px;
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: #EEEEEE;
border: none;
padding: 8px 12px;
}
QMenuBar::item:selected {
background-color: rgba(255, 255, 255, 10%);
}
/* Menu */
QMenu {
padding: 4px;
background: white;
border: 1px solid #E0E0E0;
}
QMenu::indicator {
left: 6px;
width: 20px;
height: 20px;
}
QMenu::icon {
left: 6px;
}
QMenu::item {
background: transparent;
color: #333333;
padding: 6px 24px;
}
QMenu::item:selected {
background-color: rgba(0, 0, 0, 10%);
}
QMenu::item:disabled {
color: #CCCCCC;
}
QMenu::separator {
height: 2px;
background-color: #CCCCCC;
margin: 6px 0;
}
/* Window */
MainWindow {
background-color: #F3F3F3;
}
MainWindow[custom-style=true] {
background-color: transparent;
}
QWidget#clock-widget {
font-size: 75px;
color: #333333;
font-weight: bold;
background-color: transparent;
}

View File

@ -0,0 +1,41 @@
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
// SPDX-License-Identifier: Apache-2.0
#include <QtWidgets/QApplication>
#include "mainwindow.h"
int main(int argc, char *argv[]) {
qputenv("QT_WIN_DEBUG_CONSOLE", "attach");
qputenv("QSG_INFO", "1");
#if 0
qputenv("QT_WIDGETS_RHI", "1");
qputenv("QSG_RHI_BACKEND", "d3d12");
qputenv("QSG_RHI_HDR", "scrgb");
qputenv("QT_QPA_DISABLE_REDIRECTION_SURFACE", "1");
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
QApplication a(argc, argv);
#if 0 && defined(Q_OS_WINDOWS) && QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
QApplication::setFont([]() {
QFont f("Microsoft YaHei");
f.setStyleStrategy(QFont::PreferAntialias);
f.setPixelSize(15);
return f;
}());
#endif
MainWindow w;
w.show();
#if 0
QMainWindow w2;
w2.show();
#endif
return a.exec();
}

View File

@ -0,0 +1,372 @@
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
// SPDX-License-Identifier: Apache-2.0
#include "mainwindow.h"
#include <QtCore/QDebug>
#include <QtCore/QFile>
#include <QtCore/QTime>
#include <QtCore/QTimer>
#include <QtGui/QPainter>
#include <QtGui/QWindow>
#include <QtWidgets/QApplication>
#include <QtWidgets/QStyle>
#include <QtWidgets/QPushButton>
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
# include <QtGui/QActionGroup>
#else
# include <QtWidgets/QActionGroup>
#endif
// #include <QtWebEngineWidgets/QWebEngineView>
#include <QWKWidgets/widgetwindowagent.h>
#include <widgetframe/windowbar.h>
#include <widgetframe/windowbutton.h>
class ClockWidget : public QLabel {
public:
explicit ClockWidget(QWidget *parent = nullptr) : QLabel(parent) {
startTimer(100);
setAlignment(Qt::AlignCenter);
}
~ClockWidget() override = default;
protected:
void timerEvent(QTimerEvent *event) override {
setText(QTime::currentTime().toString(QStringLiteral("hh:mm:ss")));
}
};
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent) {
installWindowAgent();
#if 1
auto clockWidget = new ClockWidget();
clockWidget->setObjectName(QStringLiteral("clock-widget"));
clockWidget->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding);
setCentralWidget(clockWidget);
#else
auto webView = new QWebEngineView();
webView->load(QUrl("https://www.baidu.com"));
setCentralWidget(webView);
#endif
loadStyleSheet(Dark);
setWindowTitle(tr("Example MainWindow"));
resize(800, 600);
// windowAgent->centralize();
}
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() = default;
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::closeEvent(QCloseEvent *event) {
// if (!(qApp->keyboardModifiers() & Qt::ControlModifier)) {
// QTimer::singleShot(1000, this, &QWidget::show);
// }
event->accept();
}
void MainWindow::installWindowAgent() {
// 1. Setup window agent
windowAgent = new QWK::WidgetWindowAgent(this);
windowAgent->setup(this);
// 2. Construct your title bar
auto menuBar = [this]() {
auto menuBar = new QMenuBar();
// Virtual menu
auto file = new QMenu(tr("File(&F)"), menuBar);
file->addAction(new QAction(tr("New(&N)"), menuBar));
file->addAction(new QAction(tr("Open(&O)"), menuBar));
file->addSeparator();
auto edit = new QMenu(tr("Edit(&E)"), menuBar);
edit->addAction(new QAction(tr("Undo(&U)"), menuBar));
edit->addAction(new QAction(tr("Redo(&R)"), menuBar));
// Theme action
auto darkAction = new QAction(tr("Enable dark theme"), menuBar);
darkAction->setCheckable(true);
connect(darkAction, &QAction::triggered, this, [this](bool checked) {
loadStyleSheet(checked ? Dark : Light); //
});
connect(this, &MainWindow::themeChanged, darkAction, [this, darkAction]() {
darkAction->setChecked(currentTheme == Dark); //
});
#ifdef Q_OS_WIN
auto dwmBlurAction = new QAction(tr("Enable DWM blur"), menuBar);
dwmBlurAction->setCheckable(true);
connect(dwmBlurAction, &QAction::toggled, this, [this](bool checked) {
if (!windowAgent->setWindowAttribute(QStringLiteral("dwm-blur"), checked)) {
return;
}
setProperty("custom-style", checked);
style()->polish(this);
});
auto acrylicAction = new QAction(tr("Enable acrylic material"), menuBar);
acrylicAction->setCheckable(true);
connect(acrylicAction, &QAction::toggled, this, [this](bool checked) {
if (!windowAgent->setWindowAttribute(QStringLiteral("acrylic-material"), true)) {
return;
}
setProperty("custom-style", checked);
style()->polish(this);
});
auto micaAction = new QAction(tr("Enable mica"), menuBar);
micaAction->setCheckable(true);
connect(micaAction, &QAction::toggled, this, [this](bool checked) {
if (!windowAgent->setWindowAttribute(QStringLiteral("mica"), checked)) {
return;
}
setProperty("custom-style", checked);
style()->polish(this);
});
auto micaAltAction = new QAction(tr("Enable mica alt"), menuBar);
micaAltAction->setCheckable(true);
connect(micaAltAction, &QAction::toggled, this, [this](bool checked) {
if (!windowAgent->setWindowAttribute(QStringLiteral("mica-alt"), checked)) {
return;
}
setProperty("custom-style", checked);
style()->polish(this);
});
#elif defined(Q_OS_MAC)
auto darkBlurAction = new QAction(tr("Dark blur"), menuBar);
darkBlurAction->setCheckable(true);
connect(darkBlurAction, &QAction::toggled, this, [this](bool checked) {
if (!windowAgent->setWindowAttribute(QStringLiteral("blur-effect"), "dark")) {
return;
}
if (checked) {
setProperty("custom-style", true);
style()->polish(this);
}
});
auto lightBlurAction = new QAction(tr("Light blur"), menuBar);
lightBlurAction->setCheckable(true);
connect(lightBlurAction, &QAction::toggled, this, [this](bool checked) {
if (!windowAgent->setWindowAttribute(QStringLiteral("blur-effect"), "light")) {
return;
}
if (checked) {
setProperty("custom-style", true);
style()->polish(this);
}
});
auto noBlurAction = new QAction(tr("No blur"), menuBar);
noBlurAction->setCheckable(true);
connect(noBlurAction, &QAction::toggled, this, [this](bool checked) {
if (!windowAgent->setWindowAttribute(QStringLiteral("blur-effect"), "none")) {
return;
}
if (checked) {
setProperty("custom-style", false);
style()->polish(this);
}
});
auto macStyleGroup = new QActionGroup(menuBar);
macStyleGroup->addAction(darkBlurAction);
macStyleGroup->addAction(lightBlurAction);
macStyleGroup->addAction(noBlurAction);
#endif
// Real menu
auto settings = new QMenu(tr("Settings(&S)"), menuBar);
settings->addAction(darkAction);
#ifdef Q_OS_WIN
settings->addSeparator();
settings->addAction(dwmBlurAction);
settings->addAction(acrylicAction);
settings->addAction(micaAction);
settings->addAction(micaAltAction);
#elif defined(Q_OS_MAC)
settings->addAction(darkBlurAction);
settings->addAction(lightBlurAction);
settings->addAction(noBlurAction);
#endif
menuBar->addMenu(file);
menuBar->addMenu(edit);
menuBar->addMenu(settings);
return menuBar;
}();
menuBar->setObjectName(QStringLiteral("win-menu-bar"));
auto titleLabel = new QLabel();
titleLabel->setAlignment(Qt::AlignCenter);
titleLabel->setObjectName(QStringLiteral("win-title-label"));
#ifndef Q_OS_MAC
auto iconButton = new QWK::WindowButton();
iconButton->setObjectName(QStringLiteral("icon-button"));
iconButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
auto minButton = new QWK::WindowButton();
minButton->setObjectName(QStringLiteral("min-button"));
minButton->setProperty("system-button", true);
minButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
auto maxButton = new QWK::WindowButton();
maxButton->setCheckable(true);
maxButton->setObjectName(QStringLiteral("max-button"));
maxButton->setProperty("system-button", true);
maxButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
auto closeButton = new QWK::WindowButton();
closeButton->setObjectName(QStringLiteral("close-button"));
closeButton->setProperty("system-button", true);
closeButton->setSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred);
#endif
auto windowBar = new QWK::WindowBar();
#ifndef Q_OS_MAC
windowBar->setIconButton(iconButton);
windowBar->setMinButton(minButton);
windowBar->setMaxButton(maxButton);
windowBar->setCloseButton(closeButton);
#endif
windowBar->setMenuBar(menuBar);
windowBar->setTitleLabel(titleLabel);
windowBar->setHostWidget(this);
windowAgent->setTitleBar(windowBar);
#ifndef Q_OS_MAC
windowAgent->setSystemButton(QWK::WindowAgentBase::WindowIcon, iconButton);
windowAgent->setSystemButton(QWK::WindowAgentBase::Minimize, minButton);
windowAgent->setSystemButton(QWK::WindowAgentBase::Maximize, maxButton);
windowAgent->setSystemButton(QWK::WindowAgentBase::Close, closeButton);
#endif
windowAgent->setHitTestVisible(menuBar, true);
#ifdef Q_OS_MAC
windowAgent->setSystemButtonAreaCallback([](const QSize &size) {
static constexpr const int width = 75;
return QRect(QPoint(size.width() - width, 0), QSize(width, size.height())); //
});
#endif
setMenuWidget(windowBar);
// 3. Adds simulated mouse events to the title bar buttons
#ifdef Q_OS_WINDOWS
// Emulate Window system menu button behaviors
connect(iconButton, &QAbstractButton::clicked, windowAgent, [this, iconButton] {
iconButton->setProperty("double-click-close", false);
// Pick a suitable time threshold
QTimer::singleShot(75, windowAgent, [this, iconButton]() {
if (iconButton->property("double-click-close").toBool())
return;
windowAgent->showSystemMenu(iconButton->mapToGlobal(QPoint{0, iconButton->height()}));
});
});
connect(iconButton, &QWK::WindowButton::doubleClicked, this, [iconButton, this]() {
iconButton->setProperty("double-click-close", true);
close();
});
#endif
#ifndef Q_OS_MAC
connect(windowBar, &QWK::WindowBar::minimizeRequested, this, &QWidget::showMinimized);
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
// manually send leave events to the button.
emulateLeaveEvent(maxButton);
});
connect(windowBar, &QWK::WindowBar::closeRequested, this, &QWidget::close);
#endif
}
void MainWindow::loadStyleSheet(Theme theme) {
if (!styleSheet().isEmpty() && theme == currentTheme)
return;
currentTheme = theme;
if (QFile qss(theme == Dark ? QStringLiteral(":/dark-style.qss")
: QStringLiteral(":/light-style.qss"));
qss.open(QIODevice::ReadOnly | QIODevice::Text)) {
setStyleSheet(QString::fromUtf8(qss.readAll()));
Q_EMIT themeChanged();
}
}

View File

@ -0,0 +1,44 @@
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
// SPDX-License-Identifier: Apache-2.0
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QtWidgets/QMainWindow>
namespace QWK {
class WidgetWindowAgent;
class StyleAgent;
}
class MainWindow : public QMainWindow {
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = nullptr);
~MainWindow() override;
enum Theme {
Dark,
Light,
};
Q_ENUM(Theme)
Q_SIGNALS:
void themeChanged();
protected:
bool event(QEvent *event) override;
void closeEvent(QCloseEvent *event) override;
private:
void installWindowAgent();
void loadStyleSheet(Theme theme);
Theme currentTheme{};
QWK::WidgetWindowAgent *windowAgent;
};
#endif // MAINWINDOW_H

View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>dark-style.qss</file>
<file>light-style.qss</file>
</qresource>
</RCC>

View File

@ -0,0 +1,14 @@
project(QWKExample_QML)
file(GLOB _src *.h *.cpp *.qrc)
qwk_add_example(${PROJECT_NAME}
SOURCES ${_src} ../shared/resources/shared.qrc
QT_LINKS Core Gui Qml Quick
LINKS QWKQuick
)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED TRUE
)

View File

@ -0,0 +1,39 @@
import QtQuick 2.15
import QtQuick.Controls 2.15
Button {
id: root
width: height * 1.5
leftPadding: 0
topPadding: 0
rightPadding: 0
bottomPadding: 0
leftInset: 0
topInset: 0
rightInset: 0
bottomInset: 0
property alias source: image.source
contentItem: Item {
Image {
id: image
anchors.centerIn: parent
mipmap: true
width: 12
height: 12
}
}
background: Rectangle {
color: {
if (!root.enabled) {
return "gray";
}
if (root.pressed) {
return Qt.rgba(0, 0, 0, 0.15);
}
if (root.hovered) {
return Qt.rgba(0, 0, 0, 0.15);
}
return "transparent";
}
}
}

View File

@ -0,0 +1,33 @@
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
// SPDX-License-Identifier: Apache-2.0
#include <QtGui/QGuiApplication>
#include <QtQml/QQmlApplicationEngine>
#include <QtQuick/QQuickWindow>
#include <QWKQuick/qwkquickglobal.h>
int main(int argc, char *argv[]) {
qputenv("QT_WIN_DEBUG_CONSOLE", "attach");
qputenv("QSG_INFO", "1");
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
qputenv("QT_QUICK_CONTROLS_STYLE", "Basic");
#else
qputenv("QT_QUICK_CONTROLS_STYLE", "Default");
#endif
#if 0
qputenv("QSG_RHI_BACKEND", "opengl");
//qputenv("QSG_RHI_HDR", "scrgb");
//qputenv("QT_QPA_DISABLE_REDIRECTION_SURFACE", "1");
QGuiApplication::setHighDpiScaleFactorRoundingPolicy(
Qt::HighDpiScaleFactorRoundingPolicy::PassThrough);
#endif
QGuiApplication application(argc, argv);
// Make sure alpha channel is requested, our special effects on Windows depends on it.
QQuickWindow::setDefaultAlphaBuffer(true);
QQmlApplicationEngine engine;
QWK::registerTypes(&engine);
engine.load(QUrl(QStringLiteral("qrc:///main.qml")));
return application.exec();
}

View File

@ -0,0 +1,220 @@
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import Qt.labs.platform 1.1
import QWindowKit 1.0
Window {
id: window
width: 800
height: 600
color: darkStyle.windowBackgroundColor
title: qsTr("Hello, world!")
Component.onCompleted: {
windowAgent.setup(window)
window.visible = true
}
QtObject {
id: lightStyle
}
QtObject {
id: darkStyle
readonly property color windowBackgroundColor: "#1E1E1E"
}
Timer {
interval: 100
running: true
repeat: true
onTriggered: timeLabel.text = Qt.formatTime(new Date(), "hh:mm:ss")
}
WindowAgent {
id: windowAgent
}
MouseArea {
anchors.fill: parent
acceptedButtons: Qt.RightButton
onClicked: contextMenu.open()
}
Rectangle {
id: titleBar
anchors {
top: parent.top
topMargin: 1
left: parent.left
right: parent.right
}
height: 32
color: window.active ? "#3C3C3C" : "#505050"
Component.onCompleted: windowAgent.setTitleBar(titleBar)
Image {
id: iconButton
anchors {
verticalCenter: parent.verticalCenter
left: parent.left
leftMargin: 10
}
width: 18
height: 18
mipmap: true
source: "qrc:///app/example.png"
}
Text {
anchors {
verticalCenter: parent.verticalCenter
left: iconButton.right
leftMargin: 10
}
horizontalAlignment: Text.AlignHCenter
verticalAlignment: Text.AlignVCenter
text: window.title
font.pixelSize: 14
color: "#ECECEC"
}
Row {
anchors {
top: parent.top
right: parent.right
}
height: parent.height
QWKButton {
id: minButton
height: parent.height
source: "qrc:///window-bar/minimize.svg"
onClicked: window.showMinimized()
Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Minimize, minButton)
}
QWKButton {
id: maxButton
height: parent.height
source: window.visibility === Window.Maximized ? "qrc:///window-bar/restore.svg" : "qrc:///window-bar/maximize.svg"
onClicked: {
if (window.visibility === Window.Maximized) {
window.showNormal()
} else {
window.showMaximized()
}
}
Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Maximize, maxButton)
}
QWKButton {
id: closeButton
height: parent.height
source: "qrc:///window-bar/close.svg"
background: Rectangle {
color: {
if (!closeButton.enabled) {
return "gray";
}
if (closeButton.pressed) {
return "#e81123";
}
if (closeButton.hovered) {
return "#e81123";
}
return "transparent";
}
}
onClicked: window.close()
Component.onCompleted: windowAgent.setSystemButton(WindowAgent.Close, closeButton)
}
}
}
Label {
id: timeLabel
anchors.centerIn: parent
font {
pointSize: 75
bold: true
}
color: "#FEFEFE"
}
Menu {
id: contextMenu
Menu {
id: themeMenu
title: qsTr("Theme")
MenuItemGroup {
id: themeMenuGroup
items: themeMenu.items
}
MenuItem {
text: qsTr("Light")
checkable: true
onTriggered: windowAgent.setWindowAttribute("dark-mode", false)
}
MenuItem {
text: qsTr("Dark")
checkable: true
onTriggered: windowAgent.setWindowAttribute("dark-mode", true)
}
}
Menu {
id: specialEffectMenu
title: qsTr("Special effect")
MenuItemGroup {
id: specialEffectMenuGroup
items: specialEffectMenu.items
}
MenuItem {
enabled: Qt.platform.os === "windows"
text: qsTr("DWM blur")
checkable: true
onTriggered: {
window.color = checked ? "transparent" : darkStyle.windowBackgroundColor
windowAgent.setWindowAttribute("dwm-blur", checked)
}
}
MenuItem {
enabled: Qt.platform.os === "windows"
text: qsTr("Acrylic material")
checkable: true
onTriggered: {
window.color = checked ? "transparent" : darkStyle.windowBackgroundColor
windowAgent.setWindowAttribute("acrylic-material", checked)
}
}
MenuItem {
enabled: Qt.platform.os === "windows"
text: qsTr("Mica")
checkable: true
onTriggered: {
window.color = checked ? "transparent" : darkStyle.windowBackgroundColor
windowAgent.setWindowAttribute("mica", checked)
}
}
MenuItem {
enabled: Qt.platform.os === "windows"
text: qsTr("Mica Alt")
checkable: true
onTriggered: {
window.color = checked ? "transparent" : darkStyle.windowBackgroundColor
windowAgent.setWindowAttribute("mica-alt", checked)
}
}
}
}
}

View File

@ -0,0 +1,6 @@
<RCC>
<qresource prefix="/">
<file>main.qml</file>
<file>QWKButton.qml</file>
</qresource>
</RCC>

View File

@ -0,0 +1 @@
add_subdirectory(widgetframe)

Binary file not shown.

After

Width:  |  Height:  |  Size: 52 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

View File

@ -0,0 +1,11 @@
<RCC>
<qresource prefix="/">
<file>window-bar/close.svg</file>
<file>window-bar/fullscreen.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>
<file>app/example.png</file>
</qresource>
</RCC>

View 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>

After

Width:  |  Height:  |  Size: 444 B

View 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>

After

Width:  |  Height:  |  Size: 1.5 KiB

View 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>

After

Width:  |  Height:  |  Size: 328 B

View 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>

After

Width:  |  Height:  |  Size: 467 B

View 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>

After

Width:  |  Height:  |  Size: 419 B

View 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>

After

Width:  |  Height:  |  Size: 559 B

View File

@ -0,0 +1,21 @@
project(WidgetFrame)
set(CMAKE_AUTOMOC ON)
set(CMAKE_AUTOUIC ON)
set(CMAKE_AUTORCC ON)
file(GLOB _src *.h *.cpp)
add_library(${PROJECT_NAME} STATIC)
qm_configure_target(${PROJECT_NAME}
SOURCES ${_src}
QT_LINKS Core Gui Widgets
)
set_target_properties(${PROJECT_NAME} PROPERTIES
CXX_STANDARD 17
CXX_STANDARD_REQUIRED TRUE
)
target_include_directories(${PROJECT_NAME} PUBLIC . ..)

View File

@ -0,0 +1,298 @@
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
// SPDX-License-Identifier: Apache-2.0
#include "windowbar.h"
#include "windowbar_p.h"
#include <QtCore/QDebug>
#include <QtCore/QLocale>
#include <QtGui/QtEvents>
namespace QWK {
WindowBarPrivate::WindowBarPrivate() {
w = nullptr;
autoTitle = true;
autoIcon = false;
}
WindowBarPrivate::~WindowBarPrivate() = default;
void WindowBarPrivate::init() {
Q_Q(WindowBar);
layout = new QHBoxLayout();
if (QLocale::system().textDirection() == Qt::RightToLeft) {
layout->setDirection(QBoxLayout::RightToLeft);
}
layout->setContentsMargins(QMargins());
layout->setSpacing(0);
for (int i = IconButton; i <= CloseButton; ++i) {
insertDefaultSpace(i);
}
q->setLayout(layout);
}
void WindowBarPrivate::setWidgetAt(int index, QWidget *widget) {
auto item = layout->takeAt(index);
auto orgWidget = item->widget();
if (orgWidget) {
orgWidget->deleteLater();
}
delete item;
if (!widget) {
insertDefaultSpace(index);
} else {
layout->insertWidget(index, widget);
}
}
QWidget *WindowBarPrivate::takeWidgetAt(int index) {
auto item = layout->itemAt(index);
auto orgWidget = item->widget();
if (orgWidget) {
item = layout->takeAt(index);
delete item;
insertDefaultSpace(index);
}
return orgWidget;
}
WindowBar::WindowBar(QWidget *parent) : WindowBar(*new WindowBarPrivate(), parent) {
}
WindowBar::~WindowBar() = default;
QMenuBar *WindowBar::menuBar() const {
Q_D(const WindowBar);
return static_cast<QMenuBar *>(d->widgetAt(WindowBarPrivate::MenuWidget));
}
QLabel *WindowBar::titleLabel() const {
Q_D(const WindowBar);
return static_cast<QLabel *>(d->widgetAt(WindowBarPrivate::TitleLabel));
}
QAbstractButton *WindowBar::iconButton() const {
Q_D(const WindowBar);
return static_cast<QAbstractButton *>(d->widgetAt(WindowBarPrivate::IconButton));
}
QAbstractButton *WindowBar::minButton() const {
Q_D(const WindowBar);
return static_cast<QAbstractButton *>(d->widgetAt(WindowBarPrivate::MinimumButton));
}
QAbstractButton *WindowBar::maxButton() const {
Q_D(const WindowBar);
return static_cast<QAbstractButton *>(d->widgetAt(WindowBarPrivate::MaximumButton));
}
QAbstractButton *WindowBar::closeButton() const {
Q_D(const WindowBar);
return static_cast<QAbstractButton *>(d->widgetAt(WindowBarPrivate::CloseButton));
}
void WindowBar::setMenuBar(QMenuBar *menuBar) {
Q_D(WindowBar);
auto org = takeMenuBar();
if (org)
org->deleteLater();
if (!menuBar)
return;
d->setWidgetAt(WindowBarPrivate::MenuWidget, menuBar);
menuBar->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Minimum);
}
void WindowBar::setTitleLabel(QLabel *label) {
Q_D(WindowBar);
auto org = takeTitleLabel();
if (org)
org->deleteLater();
if (!label)
return;
d->setWidgetAt(WindowBarPrivate::TitleLabel, label);
if (d->autoTitle && d->w)
label->setText(d->w->windowTitle());
label->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
}
void WindowBar::setIconButton(QAbstractButton *btn) {
Q_D(WindowBar);
auto org = takeIconButton();
if (org)
org->deleteLater();
if (!btn)
return;
d->setWidgetAt(WindowBarPrivate::IconButton, btn);
if (d->autoIcon && d->w)
btn->setIcon(d->w->windowIcon());
btn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
}
void WindowBar::setMinButton(QAbstractButton *btn) {
Q_D(WindowBar);
auto org = takeMinButton();
if (org)
org->deleteLater();
if (!btn)
return;
d->setWidgetAt(WindowBarPrivate::MinimumButton, btn);
connect(btn, &QAbstractButton::clicked, this, &WindowBar::minimizeRequested);
}
void WindowBar::setMaxButton(QAbstractButton *btn) {
Q_D(WindowBar);
auto org = takeMaxButton();
if (org)
org->deleteLater();
if (!btn)
return;
d->setWidgetAt(WindowBarPrivate::MaximumButton, btn);
connect(btn, &QAbstractButton::clicked, this, &WindowBar::maximizeRequested);
}
void WindowBar::setCloseButton(QAbstractButton *btn) {
Q_D(WindowBar);
auto org = takeCloseButton();
if (org)
org->deleteLater();
if (!btn)
return;
d->setWidgetAt(WindowBarPrivate::CloseButton, btn);
connect(btn, &QAbstractButton::clicked, this, &WindowBar::closeRequested);
}
QMenuBar *WindowBar::takeMenuBar() {
Q_D(WindowBar);
return static_cast<QMenuBar *>(d->takeWidgetAt(WindowBarPrivate::MenuWidget));
}
QLabel *WindowBar::takeTitleLabel() {
Q_D(WindowBar);
return static_cast<QLabel *>(d->takeWidgetAt(WindowBarPrivate::TitleLabel));
}
QAbstractButton *WindowBar::takeIconButton() {
Q_D(WindowBar);
return static_cast<QAbstractButton *>(d->takeWidgetAt(WindowBarPrivate::IconButton));
}
QAbstractButton *WindowBar::takeMinButton() {
Q_D(WindowBar);
auto btn = static_cast<QAbstractButton *>(d->takeWidgetAt(WindowBarPrivate::MinimumButton));
if (!btn) {
return nullptr;
}
disconnect(btn, &QAbstractButton::clicked, this, &WindowBar::minimizeRequested);
return btn;
}
QAbstractButton *WindowBar::takeMaxButton() {
Q_D(WindowBar);
auto btn = static_cast<QAbstractButton *>(d->takeWidgetAt(WindowBarPrivate::MaximumButton));
if (!btn) {
return nullptr;
}
disconnect(btn, &QAbstractButton::clicked, this, &WindowBar::maximizeRequested);
return btn;
}
QAbstractButton *WindowBar::takeCloseButton() {
Q_D(WindowBar);
auto btn = static_cast<QAbstractButton *>(d->takeWidgetAt(WindowBarPrivate::CloseButton));
if (!btn) {
return nullptr;
}
disconnect(btn, &QAbstractButton::clicked, this, &WindowBar::closeRequested);
return btn;
}
QWidget *WindowBar::hostWidget() const {
Q_D(const WindowBar);
return d->w;
}
void WindowBar::setHostWidget(QWidget *w) {
Q_D(WindowBar);
QWidget *org = d->w;
if (org) {
org->removeEventFilter(this);
}
d_ptr->w = w;
if (w) {
w->installEventFilter(this);
}
}
bool WindowBar::titleFollowWindow() const {
Q_D(const WindowBar);
return d->autoTitle;
}
void WindowBar::setTitleFollowWindow(bool value) {
Q_D(WindowBar);
d->autoTitle = value;
}
bool WindowBar::iconFollowWindow() const {
Q_D(const WindowBar);
return d->autoIcon;
}
void WindowBar::setIconFollowWindow(bool value) {
Q_D(WindowBar);
d->autoIcon = value;
}
bool WindowBar::eventFilter(QObject *obj, QEvent *event) {
Q_D(WindowBar);
auto w = d->w;
if (obj == w) {
QAbstractButton *iconBtn = iconButton();
QLabel *label = titleLabel();
QAbstractButton *maxBtn = maxButton();
switch (event->type()) {
case QEvent::WindowIconChange: {
if (d_ptr->autoIcon && iconBtn) {
iconBtn->setIcon(w->windowIcon());
iconChanged(w->windowIcon());
}
break;
}
case QEvent::WindowTitleChange: {
if (d_ptr->autoTitle && label) {
label->setText(w->windowTitle());
titleChanged(w->windowTitle());
}
break;
}
case QEvent::WindowStateChange: {
if (maxBtn) {
maxBtn->setChecked(w->isMaximized());
}
break;
}
default:
break;
}
}
return QWidget::eventFilter(obj, event);
}
void WindowBar::titleChanged(const QString &text) {
Q_UNUSED(text)
}
void WindowBar::iconChanged(const QIcon &icon){Q_UNUSED(icon)}
WindowBar::WindowBar(WindowBarPrivate &d, QWidget *parent)
: QFrame(parent), d_ptr(&d) {
d.q_ptr = this;
d.init();
}
}

View File

@ -0,0 +1,74 @@
// Copyright (C) 2023-2024 Stdware Collections (https://www.github.com/stdware)
// Copyright (C) 2021-2023 wangwenx190 (Yuhang Zhao)
// SPDX-License-Identifier: Apache-2.0
#ifndef WINDOWBAR_H
#define WINDOWBAR_H
#include <QFrame>
#include <QAbstractButton>
#include <QMenuBar>
#include <QLabel>
namespace QWK {
class WindowBarPrivate;
class WindowBar : public QFrame {
Q_OBJECT
Q_DECLARE_PRIVATE(WindowBar)
public:
explicit WindowBar(QWidget *parent = nullptr);
~WindowBar();
public:
QMenuBar *menuBar() const;
QLabel *titleLabel() const;
QAbstractButton *iconButton() const;
QAbstractButton *minButton() const;
QAbstractButton *maxButton() const;
QAbstractButton *closeButton() const;
void setMenuBar(QMenuBar *menuBar);
void setTitleLabel(QLabel *label);
void setIconButton(QAbstractButton *btn);
void setMinButton(QAbstractButton *btn);
void setMaxButton(QAbstractButton *btn);
void setCloseButton(QAbstractButton *btn);
QMenuBar *takeMenuBar();
QLabel *takeTitleLabel();
QAbstractButton *takeIconButton();
QAbstractButton *takeMinButton();
QAbstractButton *takeMaxButton();
QAbstractButton *takeCloseButton();
QWidget *hostWidget() const;
void setHostWidget(QWidget *w);
bool titleFollowWindow() const;
void setTitleFollowWindow(bool value);
bool iconFollowWindow() const;
void setIconFollowWindow(bool value);
Q_SIGNALS:
void minimizeRequested();
void maximizeRequested(bool max = false);
void closeRequested();
protected:
bool eventFilter(QObject *obj, QEvent *event) override;
virtual void titleChanged(const QString &text);
virtual void iconChanged(const QIcon &icon);
protected:
WindowBar(WindowBarPrivate &d, QWidget *parent = nullptr);
QScopedPointer<WindowBarPrivate> d_ptr;
};
}
#endif // WINDOWBAR_H

Some files were not shown because too many files have changed in this diff Show More