This commit is contained in:
ivaquero
2026-04-18 06:13:30 +08:00
parent 49b39b74d2
commit ec341ddba2
36 changed files with 1941 additions and 273 deletions
+116
View File
@@ -0,0 +1,116 @@
# GitHub Actions Build System
This project uses GitHub Actions for automated building and releasing across multiple platforms.
## Available Workflows
### 1. CI Build (`ci.yml`)
**Trigger**: Push to main/master branches, pull requests
**Purpose**: Quick validation builds on all platforms
**Artifacts**: Platform-specific executables
### 2. Build Qt (`build.yml`)
**Trigger**: Push to main/master branches, pull requests, manual dispatch
**Purpose**: Comprehensive builds using both qmake and CMake
**Artifacts**:
- qmake builds for each platform
- CMake builds for each platform
### 3. Release (`release.yml`)
**Trigger**: Git tags (v*), manual dispatch
**Purpose**: Create official releases with distributable packages
**Artifacts**:
- macOS: DMG installer
- Windows: ZIP package with dependencies
- Linux: TAR.GZ package
## Usage
### Automatic Builds
Every push to main/master branches triggers CI builds to ensure code quality.
### Creating a Release
1. Create and push a version tag:
```bash
git tag v1.0.0
git push origin v1.0.0
```
2. The release workflow will automatically:
- Build for all platforms
- Create distributable packages
- Create a GitHub release with artifacts
### Manual Release
1. Go to Actions tab in GitHub
2. Select "Build and Release Clouck"
3. Click "Run workflow"
4. Enter version number (e.g., v1.0.0)
5. Click "Run workflow"
## Build Requirements
### macOS
- Qt 6.5.0
- Xcode Command Line Tools
- macdeployqt for app bundling
### Windows
- Qt 6.5.0
- Visual Studio Build Tools or MinGW
- windeployqt for dependency deployment
### Linux
- Qt 6.5.0
- GCC/Clang
- X11 development libraries
- XFixes development libraries
## Artifacts
Each workflow produces platform-specific artifacts:
- **macOS**: `.app` bundle or `.dmg` installer
- **Windows**: Executable with Qt dependencies
- **Linux**: Executable with shared libraries
## Configuration
The workflows use:
- `jurplel/install-qt-action@v4` for Qt installation
- Matrix builds for multi-platform support
- Artifact upload for build retention
- Automatic release creation for tagged builds
## Troubleshooting
### Build Failures
1. Check Qt version compatibility
2. Verify platform-specific dependencies
3. Review build logs in GitHub Actions
### Missing Dependencies
- Ensure all required Qt modules are specified
- Check platform-specific library installations
- Verify CMake/qmake configuration
### Release Issues
- Ensure proper version tagging format (v*)
- Check GitHub token permissions
- Verify artifact upload paths
+143
View File
@@ -0,0 +1,143 @@
name: Build Qt Application
on:
push:
branches: [ main, master ]
pull_request:
branches: [ main, master ]
workflow_dispatch:
jobs:
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: "6.5.*"
modules: "qt5compat"
cache: true
tools: "tools_ninja"
install-deps: "true"
- name: Build with qmake
run: |
qmake Clouck.pro CONFIG+=release
make
- name: Create macOS Bundle
run: |
macdeployqt build/Clouck.app -dmg
- name: Upload macOS Build
uses: actions/upload-artifact@v7
with:
name: Clouck-qmake
path: build/Clouck.dmg
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- name: Setup MSVC Environment
uses: ilammy/msvc-dev-cmd@v1
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: "6.5.*"
modules: "qt5compat"
cache: true
tools: "tools_ninja"
install-deps: "true"
- name: Build with qmake
shell: cmd
run: |
qmake Clouck.pro CONFIG+=release
nmake
- name: Deploy Windows Build
run: |
mkdir Clouck-windows-dist
copy build\Clouck.exe Clouck-windows-dist\
windeployqt --release Clouck-windows-dist\Clouck.exe
- name: Upload Windows Build
uses: actions/upload-artifact@v7
with:
name: Clouck-windows-qmake
path: Clouck-windows-dist
# build-linux:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v6
# - name: Install Dependencies
# run: |
# sudo apt-get update
# sudo apt-get install -y build-essential qt6-base-dev qt6-tools-dev libx11-dev libxfixes-dev
# - name: Install Qt
# uses: jurplel/install-qt-action@v4
# with:
# version: "6.5.*"
# modules: "qt5compat"
# cache: true
# - name: Build with qmake
# run: |
# qmake Clouck.pro CONFIG+=release
# make
# - name: Create Linux Distribution
# run: |
# mkdir -p Clouck-linux-dist
# cp Clouck Clouck-linux-dist/
# ldd Clouck | grep -o '/lib[^ ]*' | xargs -I {} cp {} Clouck-linux-dist/ 2>/dev/null || true
# - name: Upload Linux Build
# uses: actions/upload-artifact@v7
# with:
# name: Clouck-linux-qmake
# path: Clouck-linux-dist
build-cmake:
strategy:
matrix:
os: [ windows-latest, macos-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: "6.5.*"
modules: "qt5compat"
cache: true
- name: Install Linux Dependencies
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y build-essential cmake libx11-dev libxfixes-dev
- name: Configure CMake
run: |
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
- name: Build with CMake
run: cmake --build build --config Release
- name: Upload CMake Build
uses: actions/upload-artifact@v7
with:
name: Clouck-${{ runner.os }}-cmake
path: build/Clouck*
+200
View File
@@ -0,0 +1,200 @@
name: CI Build
on:
push:
branches: [ main, master ]
paths:
- "**.cpp"
- "**.h"
- "**.pro"
- "CMakeLists.txt"
- ".github/workflows/ci.yml"
pull_request:
branches: [ main, master ]
paths:
- "**.cpp"
- "**.h"
- "**.pro"
- "CMakeLists.txt"
- ".github/workflows/ci.yml"
jobs:
build:
strategy:
fail-fast: false
matrix:
os: [ windows-latest, macos-latest ]
runs-on: ${{ matrix.os }}
steps:
- uses: actions/checkout@v6
- name: Setup MSVC Environment
if: runner.os == 'Windows'
uses: ilammy/msvc-dev-cmd@v1
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: "6.5.*"
modules: "qt5compat"
cache: true
tools: "tools_ninja"
install-deps: "true"
aqtversion: "==3.1.*"
py7zrversion: ">=0.20.2"
# - name: Install Linux Dependencies
# if: runner.os == 'Linux'
# run: |
# sudo apt-get update
# sudo apt-get install -y build-essential cmake libx11-dev libxfixes-dev
- name: Configure CMake (Windows)
if: runner.os == 'Windows'
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
echo "Configuring CMake for Windows..."
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
echo "CMake configuration completed."
echo "Checking if build directory was created:"
dir build 2>nul && echo "Build directory exists" || echo "Build directory NOT found"
echo "Contents of current directory:"
dir
- name: Configure CMake (macOS)
if: runner.os == 'macOS'
run: cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
- name: Build (Windows)
if: runner.os == 'Windows'
shell: cmd
run: |
call "C:\Program Files\Microsoft Visual Studio\2022\Enterprise\VC\Auxiliary\Build\vcvars64.bat"
echo "Starting Windows build..."
echo "=== Pre-build diagnostics ==="
echo "Current directory: %CD%"
echo "Checking if CMakeLists.txt exists:"
dir CMakeLists.txt 2>nul && echo "✓ CMakeLists.txt found" || echo "✗ CMakeLists.txt NOT found"
echo "Contents before build:"
dir
echo "=== Building with CMake ==="
cmake --build build --config Release
echo "=== Post-build diagnostics ==="
echo "Build completed with exit code: %ERRORLEVEL%"
echo "Contents after build:"
dir
echo "=== Searching for Clouck executable ==="
echo "Checking build directory structure:"
dir build /s /b 2>nul || echo "Build directory not found or empty"
echo "Looking for Clouck.exe in specific locations:"
dir build\Clouck.exe 2>nul && echo "✓ Found Clouck.exe in build\" || echo "✗ Clouck.exe not in build\"
dir build\Release\Clouck.exe 2>nul && echo "✓ Found Clouck.exe in build\Release\" || echo "✗ Clouck.exe not in build\Release\"
dir build\MinSizeRel\Clouck.exe 2>nul && echo "✓ Found Clouck.exe in build\MinSizeRel\" || echo "✗ Clouck.exe not in build\MinSizeRel\"
echo "Searching entire directory tree for any Clouck.exe:"
dir Clouck.exe /s /b 2>nul || echo "No Clouck.exe found anywhere"
echo "Searching for any .exe files in build directory:"
dir build\*.exe /s /b 2>nul || echo "No .exe files found in build"
- name: Build (macOS)
if: runner.os == 'macOS'
run: cmake --build build --config Release
- name: Quick Binary Validation (macOS)
if: runner.os == 'macOS'
run: find ./build -name "Clouck" -type f
- name: Test Build (macOS)
if: runner.os == 'macOS'
run: timeout 3s ./build/Clouck.app/Contents/MacOS/Clouck || echo "Test
completed"
- name: Quick Binary Validation (Windows)
if: runner.os == 'Windows'
shell: cmd
run: |
echo "=== Windows Quick Binary Validation ==="
echo "Current directory: %CD%"
echo "Looking for build directory..."
dir build 2>nul && echo "Build directory exists" || echo "Build directory NOT found"
echo "Looking for Clouck executable in possible locations:"
dir build\Clouck.exe 2>nul && echo "✓ Found Clouck.exe in build\" || echo "✗ Clouck.exe not in build\"
dir build\Release\Clouck.exe 2>nul && echo "✓ Found Clouck.exe in build\Release\" || echo "✗ Clouck.exe not in build\Release\"
dir build\MinSizeRel\Clouck.exe 2>nul && echo "✓ Found Clouck.exe in build\MinSizeRel\" || echo "✗ Clouck.exe not in build\MinSizeRel\"
echo "Searching entire build directory for any .exe files:"
dir build\*.exe /s /b 2>nul || echo "No .exe files found in build directory"
echo "=== End Validation ==="
- name: Test Build (Windows)
if: runner.os == 'Windows'
shell: cmd
run: |
echo "=== Windows Test Build Diagnostics ==="
echo "Current directory: %CD%"
echo "Visual Studio environment is configured (see env vars above)"
echo "Checking for build directory..."
if exist build (
echo "Build directory exists"
echo "Contents of build directory:"
dir build
echo "Looking for Clouck.exe in build directory..."
if exist build\Clouck.exe (
echo "Found Clouck.exe in build\"
echo "Testing Windows binary..."
start /min build\Clouck.exe & timeout /t 2 /nobreak >nul 2>&1 & tasklist | findstr Clouck.exe >nul 2>&1
) else (
echo "Clouck.exe not found in build\"
echo "Checking build\Release directory..."
if exist build\Release\Clouck.exe (
echo "Found Clouck.exe in build\Release\"
echo "Testing Windows binary..."
start /min build\Release\Clouck.exe & timeout /t 2 /nobreak >nul 2>&1 & tasklist | findstr Clouck.exe >nul 2>&1
) else (
echo "Clouck.exe not found in build\Release\"
echo "Checking build\MinSizeRel directory..."
if exist build\MinSizeRel\Clouck.exe (
echo "Found Clouck.exe in build\MinSizeRel\"
echo "Testing Windows binary..."
start /min build\MinSizeRel\Clouck.exe & timeout /t 2 /nobreak >nul 2>&1 & tasklist | findstr Clouck.exe >nul 2>&1
) else (
echo "ERROR: Clouck.exe not found in any expected location!"
echo "Searching entire build directory for any .exe files:"
dir build\*.exe /s /b
echo "Searching entire working directory for Clouck.exe:"
dir Clouck.exe /s /b
echo "Build failed - no executable found"
exit 1
)
)
)
) else (
echo "ERROR: Build directory does not exist!"
echo "Contents of current directory:"
dir
exit 1
)
- name: Upload Build Artifact (Windows)
if: runner.os == 'Windows'
uses: actions/upload-artifact@v7
with:
name: Clouck-windows
path: build/Clouck*.exe
- name: Upload Build Artifact (macOS)
if: runner.os == 'macOS'
run: |
echo "Checking for macOS app bundle..."
ls -la ./build/Clouck.app/Contents/MacOS/Clouck || echo "Clouck.app not found"
- name: Upload macOS Artifact
if: runner.os == 'macOS'
uses: actions/upload-artifact@v7
with:
name: Clouck-macos
path: |
build/Clouck.app/**/*
!build/Clouck.app/Contents/Frameworks/*
!build/Clouck.app/Contents/PlugIns/*
+285
View File
@@ -0,0 +1,285 @@
name: Build and Release Clouck
on:
push:
tags:
- "v*"
workflow_dispatch:
inputs:
version:
description: "Version to build (e.g., v1.0.0)"
required: true
default: "v1.0.0"
jobs:
build-macos:
runs-on: macos-latest
steps:
- uses: actions/checkout@v6
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: "6.5.*"
cache: true
- name: Configure CMake
run: |
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --config Release
- name: Create macOS App Bundle
run: |
# Debug: Check current directory and build output
echo "Current directory: $(pwd)"
echo "Build directory contents:"
ls -la build/
echo "Checking if Clouck executable exists:"
ls -la build/Clouck || echo "ERROR: Clouck executable not found!"
# Create app bundle structure
mkdir -p Clouck.app/Contents/MacOS
mkdir -p Clouck.app/Contents/Resources
# Copy executable
cp build/Clouck Clouck.app/Contents/MacOS/
# Verify executable was copied
echo "App bundle contents:"
ls -la Clouck.app/Contents/MacOS/
# Create Info.plist
cat > Clouck.app/Contents/Info.plist << EOF
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleExecutable</key>
<string>Clouck</string>
<key>CFBundleIdentifier</key>
<string>com.clouck.app</string>
<key>CFBundleName</key>
<string>Clouck</string>
<key>CFBundleVersion</key>
<string>1.0.0</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>LSUIElement</key>
<true/>
</dict>
</plist>
EOF
# Verify app bundle exists before running macdeployqt
echo "Verifying app bundle structure:"
if [ -d "Clouck.app" ]; then
echo "App bundle directory exists"
ls -la Clouck.app/
ls -la Clouck.app/Contents/
ls -la Clouck.app/Contents/MacOS/
else
echo "ERROR: Clouck.app directory not found!"
exit 1
fi
# Check if macdeployqt is available
echo "Checking macdeployqt availability:"
which macdeployqt || echo "macdeployqt not found in PATH"
# Deploy Qt dependencies (ignore non-critical errors)
echo "Running macdeployqt..."
echo "Current directory before macdeployqt: $(pwd)"
echo "Listing current directory contents:"
ls -la
echo "Calling: macdeployqt Clouck.app -dmg"
macdeployqt build/Clouck.app -dmg || echo "macdeployqt failed, will use fallback"
# Check what was created
echo "Files after macdeployqt:"
ls -la *.dmg 2>/dev/null || echo "No DMG files found"
- name: Check DMG Creation
run: |
echo "Checking for DMG files after macdeployqt:"
ls -la *.dmg 2>/dev/null || echo "No DMG files found"
# Check if DMG was created by macdeployqt
if [ -f "Clouck.dmg" ]; then
echo "Found Clouck.dmg, renaming to Clouck.dmg"
mv Clouck.dmg Clouck.dmg
echo "DMG created successfully"
else
echo "Clouck.dmg not found, checking for other DMG files:"
ls -la *.dmg 2>/dev/null || echo "Still no DMG files found"
echo "Creating DMG manually using hdiutil..."
# Fallback: create DMG manually if macdeployqt failed
hdiutil create -volname "Clouck" -srcfolder Clouck.app -ov -format UDZO Clouck.dmg
fi
- name: Upload macOS Artifact
uses: actions/upload-artifact@v7
with:
name: Clouck
path: Clouck.dmg
build-windows:
runs-on: windows-latest
steps:
- uses: actions/checkout@v6
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: "6.5.*"
cache: true
- name: Configure CMake
run: |
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
- name: Build
run: cmake --build build --config Release
- name: Create Windows Installer
run: |
# Copy executable and dependencies
mkdir Clouck-windows
cp build/Release/Clouck.exe Clouck-windows/
cp build/Release/*.dll Clouck-windows/ 2>/dev/null || true
# Deploy Qt dependencies
windeployqt --release Clouck-windows/Clouck.exe
# Create ZIP archive
Compress-Archive -Path Clouck-windows -DestinationPath Clouck-windows.zip
- name: Upload Windows Artifact
uses: actions/upload-artifact@v7
with:
name: Clouck-windows
path: Clouck-windows.zip
# build-linux:
# runs-on: ubuntu-latest
# steps:
# - uses: actions/checkout@v6
#
# - name: Install Dependencies
# run: |
# sudo apt-get update
# sudo apt-get install -y build-essential cmake qt6-base-dev qt6-tools-dev libx11-dev libxfixes-dev
#
# - name: Install Qt
# uses: jurplel/install-qt-action@v4
# with:
# version: "6.5.*"
# cache: true
#
# - name: Configure CMake
# run: |
# cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
#
# - name: Build
# run: cmake --build build --config Release
#
# - name: Create Linux AppImage
# run: |
# mkdir -p Clouck.AppDir/usr/bin
# mkdir -p Clouck.AppDir/usr/lib
#
# cp build/Clouck Clouck.AppDir/usr/bin/
#
# cat > Clouck.AppDir/clouck.desktop << EOF
# [Desktop Entry]
# Name=Clouck
# Exec=Clouck
# Icon=clouck
# Type=Application
# Categories=Utility;
# EOF
#
# # Copy desktop file
# cp Clouck.AppDir/clouck.desktop Clouck.AppDir/
#
# # Deploy Qt dependencies (simplified)
# ldd build/Clouck | grep -o '/lib[^ ]*' | xargs -I {} cp {} Clouck.AppDir/usr/lib/ 2>/dev/null || true
#
# # Create AppImage (simplified approach)
# cd Clouck.AppDir
# ln -s usr/bin/Clouck AppRun
# cd ..
#
# # Create tar.gz for distribution
# tar -czf Clouck-linux.tar.gz Clouck.AppDir/
#
# - name: Upload Linux Artifact
# uses: actions/upload-artifact@v7
# with:
# name: Clouck-linux
# path: Clouck-linux.tar.gz
create-release:
needs: [ build-macos, build-windows ]
runs-on: ubuntu-latest
if: startsWith(github.ref, 'refs/tags/v') || github.event_name ==
'workflow_dispatch'
steps:
- name: Download All Artifacts
uses: actions/download-artifact@v8
with:
path: artifacts
- name: Get Version
id: version
run: |
if [ "${{ github.event_name }}" == "workflow_dispatch" ]; then
echo "VERSION=${{ github.event.inputs.version }}" >> $GITHUB_OUTPUT
else
echo "VERSION=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
fi
- name: Create Release
uses: softprops/action-gh-release@v3
with:
name: Clouck ${{ steps.version.outputs.VERSION }}
body: |
Cross-platform clock widget application
## Features
- Always-on-top functionality across all platforms
- Fullscreen mode compatibility
- Resizable interface
- Context menu with customization options
- Native platform integration
## Downloads
- **macOS**: Clouck.dmg
- **Windows**: Clouck-windows.zip
- **Linux**: Clouck-linux.tar.gz
## Installation
### macOS
1. Download Clouck.dmg
2. Open the DMG file
3. Drag Clouck.app to Applications folder
### Windows
1. Download Clouck-windows.zip
2. Extract the ZIP file
3. Run Clouck.exe
### Linux
1. Download Clouck-linux.tar.gz
2. Extract the tar.gz file
3. Run the Clouck executable
files: |
artifacts/Clouck/Clouck.dmg
artifacts/Clouck-windows/Clouck-windows.zip
artifacts/Clouck-linux/Clouck-linux.tar.gz
draft: false
prerelease: false
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+3 -106
View File
@@ -47,48 +47,7 @@
*.idb
*.pdb
# Kernel Module Compile Results
*.mod*
*.cmd
.tmp_versions/
modules.order
Module.symvers
Mkfile.old
dkms.conf
### C++ ###
# Prerequisites
# Compiled Object files
*.slo
# Precompiled Headers
# Compiled Dynamic libraries
# Fortran module files
*.mod
*.smod
# Compiled Static libraries
*.lai
# Executables
## Various built-in generators
# cmake_find_package generator https://docs.conan.io/en/latest/reference/generators/cmake_find_package.html
Find*.cmake
# cmake_paths generator https://docs.conan.io/en/1.4/integrations/cmake/cmake_paths_generator.html
conan_paths.*
# Environment activation scripts produced by https://docs.conan.io/en/latest/mastering/virtualenv.html#virtualenv-generator
activate_run.ps1
activate_run.sh
deactivate_run.ps1
deactivate_run.sh
environment_run.ps1.env
environment_run.sh.env
# C++ objects and libs
*.slo
@@ -102,31 +61,6 @@ environment_run.sh.env
*.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
@@ -137,14 +71,6 @@ target_wrapper.*
# 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
@@ -157,39 +83,10 @@ 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 Patch ###
# iCloud generated files
*.icloud
CMakeLists.txt.user
CMakeCache.txt
CMakeFiles
CMakeScripts
Testing
Makefile
cmake_install.cmake
install_manifest.txt
compile_commands.json
CTestTestfile.cmake
_deps
##
.cache
.cmake
.qtc_clangd
.qmake.stash
build
*.dmg
+46 -11
View File
@@ -1,5 +1,5 @@
cmake_minimum_required(VERSION 3.16)
project(flock)
project(Clouck)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
@@ -7,34 +7,69 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
# Find Qt6
find_package(Qt6 REQUIRED COMPONENTS Core Widgets)
# Platform-specific dependencies
if(WIN32)
# Windows-specific libraries
set(PLATFORM_LIBS user32 dwmapi gdi32 kernel32)
elseif(UNIX AND NOT APPLE)
# Linux-specific libraries
find_package(X11 REQUIRED)
set(PLATFORM_LIBS ${X11_LIBRARIES} Xfixes)
endif()
# Set up Qt6
qt6_standard_project_setup()
# Sources
set(SOURCES
main.cpp
clockwidget.cpp
configmanager.cpp
clock_widget.cpp
config_manager.cpp
window_helper.cpp
)
set(HEADERS
clockwidget.h
configmanager.h
clock_widget.h
config_manager.h
window_helper.h
)
# Platform-specific headers
if(WIN32)
list(APPEND HEADERS window_helper_windows.h)
elseif(UNIX AND NOT APPLE)
list(APPEND HEADERS window_helper_linux.h)
elseif(APPLE)
list(APPEND HEADERS window_helper_macos.h)
endif()
# Create executable
qt6_add_executable(flock
qt6_add_executable(Clouck WIN32 MACOSX_BUNDLE
${SOURCES}
${HEADERS}
)
# Platform-specific sources and libraries
if(APPLE)
target_sources(Clouck PRIVATE window_helper_macos.mm)
target_link_libraries(Clouck PRIVATE "-framework AppKit" "-framework CoreGraphics")
endif()
if(WIN32)
target_sources(Clouck PRIVATE window_helper_windows.cpp)
target_link_libraries(Clouck PRIVATE user32 dwmapi)
endif()
if(UNIX AND NOT APPLE)
target_sources(Clouck PRIVATE window_helper_linux.cpp)
target_link_libraries(Clouck PRIVATE ${X11_LIBRARIES} Xfixes)
endif()
# Link Qt libraries
target_link_libraries(flock
Qt6::Core
Qt6::Widgets
)
target_link_libraries(Clouck PRIVATE Qt6::Core Qt6::Widgets)
# Set up install
install(TARGETS flock
install(TARGETS Clouck
RUNTIME DESTINATION bin
BUNDLE DESTINATION bin
)
+47
View File
@@ -0,0 +1,47 @@
# Created by and for Qt Creator This file was created for editing the project sources only.
# You may attempt to use it for building too, by modifying this file here.
TARGET = Clouck
QT = core gui widgets
SOURCES = \
$$PWD/main.cpp \
$$PWD/clock_widget.cpp \
$$PWD/config_manager.cpp \
$$PWD/window_helper.cpp
HEADERS = \
$$PWD/clock_widget.h \
$$PWD/config_manager.h \
$$PWD/window_helper.h \
$$PWD/window_helper_macos.h \
$$PWD/window_helper_windows.h \
$$PWD/window_helper_linux.h
macx {
OBJECTIVE_SOURCES = $$PWD/window_helper_macos.mm
LIBS += -framework AppKit -framework CoreGraphics
QMAKE_CXXFLAGS += -x objective-c++
}
win32 {
SOURCES += $$PWD/window_helper_windows.cpp
LIBS += -luser32 -ldwmapi -luser32 -lgdi32 -lkernel32
}
unix:!macx {
LIBS += -lX11 -lXfixes
}
INCLUDEPATH = \
$$PWD/.
# Set build output directories
DESTDIR = $$PWD/build
OBJECTS_DIR = $$PWD/build
MOC_DIR = $$PWD/build
RCC_DIR = $$PWD/build
UI_DIR = $$PWD/build
#DEFINES =
+32
View File
@@ -0,0 +1,32 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>CFBundleDevelopmentRegion</key>
<string>en</string>
<key>CFBundleExecutable</key>
<string>${MACOSX_BUNDLE_EXECUTABLE_NAME}</string>
<key>CFBundleIconFile</key>
<string>${MACOSX_BUNDLE_ICON_FILE}</string>
<key>CFBundleIdentifier</key>
<string>${MACOSX_BUNDLE_GUI_IDENTIFIER}</string>
<key>CFBundleName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleDisplayName</key>
<string>${MACOSX_BUNDLE_BUNDLE_NAME}</string>
<key>CFBundleVersion</key>
<string>${MACOSX_BUNDLE_BUNDLE_VERSION}</string>
<key>CFBundleShortVersionString</key>
<string>${MACOSX_BUNDLE_SHORT_VERSION_STRING}</string>
<key>CFBundlePackageType</key>
<string>APPL</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>LSMinimumSystemVersion</key>
<string>${CMAKE_OSX_DEPLOYMENT_TARGET}</string>
<key>NSPrincipalClass</key>
<string>NSApplication</string>
<key>NSHighResolutionCapable</key>
<true/>
</dict>
</plist>
+12 -12
View File
@@ -1,4 +1,4 @@
# QFlock - Qt6 浮动时钟
# Clouck 浮动时钟
## 功能特性
@@ -38,8 +38,8 @@
./build.sh
# 运行应用
./build/flock.app/Contents/MacOS/flock # macOS
./build/flock # Linux
./build/Clouck.app/Contents/MacOS/Clouck # macOS
./build/Clouck # Linux
```
#### 手动构建:
@@ -49,14 +49,14 @@
./clean.sh
# 生成Makefile
qmake flock.pro
qmake Clouck.pro
# 编译
make
# 运行
./build/flock.app/Contents/MacOS/flock # macOS
./build/flock # Linux
./build/Clouck.app/Contents/MacOS/Clouck # macOS
./build/Clouck # Linux
```
#### 使用CMake构建:
@@ -65,22 +65,22 @@ make
mkdir build && cd build
cmake ..
make
./flock
./Clouck
```
### 项目结构
```text
qflock/
qClouck/
├── build/ # 构建输出目录
│ ├── flock.app/ # macOS应用包
│ ├── Clouck.app/ # macOS应用包
│ ├── *.o # 目标文件
│ ├── moc_*.cpp # Qt元对象编译器输出
│ └── Makefile # 构建文件
├── src/ # 源代码(建议)
├── build.sh # 一键构建脚本
├── clean.sh # 清理脚本
├── flock.pro # qmake项目文件
├── Clouck.pro # qmake项目文件
├── CMakeLists.txt # CMake项目文件
└── README.md
```
@@ -98,13 +98,13 @@ qflock/
### 构建输出管理
- 所有编译生成的文件(.o, moc_*.cpp, Makefile等)都会输出到`build/`目录
- 可执行文件位于`build/flock.app/Contents/MacOS/flock`macOS)或`build/flock`Linux
- 可执行文件位于`build/Clouck.app/Contents/MacOS/Clouck`macOS)或`build/Clouck`Linux
- 使用`./clean.sh`可以快速清理所有构建文件
- 使用`./build.sh`可以进行完整的一键构建
### 自定义构建配置
可以在`flock.pro`中修改以下配置:
可以在`Clouck.pro`中修改以下配置:
- `DESTDIR`:可执行文件输出目录
- `OBJECTS_DIR`:目标文件输出目录
+39 -6
View File
@@ -1,7 +1,9 @@
# ⏰ Flock
# ☁ Clouck
![code size](https://img.shields.io/github/languages/code-size/ivaquero/floating-clock.svg)
![repo size](https://img.shields.io/github/repo-size/ivaquero/floating-clock.svg)
![Build Qt](https://github.com/ivaquero/clouck/workflows/Build%20Qt%20Application/badge.svg)
![Release](https://github.com/ivaquero/clouck/workflows/Build%20and%20Release%20Clouck/badge.svg)
![code size](https://img.shields.io/github/languages/code-size/ivaquero/clouck.svg)
![repo size](https://img.shields.io/github/repo-size/ivaquero/clouck.svg)
This project aims to build a cross-platform floating clock based on Qt6.
@@ -15,10 +17,11 @@ When on macOS / Linux, the clock on the menu bar is too small to be noticeable,
## ✨ Features
- [ ] **Main Window**
- [ ] Frameless
- [x] Always On Top
- [x] Frameless
- [x] Drag and Move
- [x] Right Click Menu
- [x] Always On Top
- [x] Resizable
- [ ] Show in Fullscreen Mode
- [x] **Style**
- [x] Set Background Color
@@ -30,7 +33,6 @@ When on macOS / Linux, the clock on the menu bar is too small to be noticeable,
- [x] Reset Settings
- [ ] **Time**
- [ ] Select Time Zone
- [ ] Set Alarms
## Quick Start
@@ -73,6 +75,37 @@ make
./flock
```
## 🚀 GitHub Actions
This project uses GitHub Actions for automated building and releasing:
### CI/CD Workflows
- **CI Build**: Validates builds on all platforms for every push/PR
- **Build Qt**: Comprehensive builds using both qmake and CMake
- **Release**: Creates distributable packages for tagged releases
### Download Pre-built Binaries
Visit the [Releases](https://github.com/ivaquero/clouck/releases) page to download pre-built binaries for:
- **macOS**: DMG installer with native app bundle
- **Windows**: ZIP package with all dependencies
- **Linux**: TAR.GZ package with shared libraries
### Creating a Release
1. Tag a new version:
```bash
git tag v1.0.0
git push origin v1.0.0
```
2. GitHub Actions will automatically build and create a release with binaries for all platforms
See [`.github/workflows/`](.github/workflows/) for detailed workflow configuration.
### Configuration
The application now uses XML configuration files instead of QSettings. Configuration is stored in `config.xml` with the following options:
-78
View File
@@ -1,78 +0,0 @@
# XML Configuration Implementation
## Overview
The configuration system has been successfully implemented using XML files instead of QSettings. The new ConfigManager class handles all configuration operations.
## Files Created/Modified
### New Files
- [`configmanager.h`](file:///Users/integzz/Documents/GitHub/qflock/configmanager.h) - Configuration manager header
- [`configmanager.cpp`](file:///Users/integzz/Documents/GitHub/qflock/configmanager.cpp) - Configuration manager implementation
- [`config.xml`](file:///Users/integzz/Documents/GitHub/qflock/config.xml) - Default configuration file
### Modified Files
- [`clockwidget.h`](file:///Users/integzz/Documents/GitHub/qflock/clockwidget.h) - Replaced QSettings with ConfigManager
- [`clockwidget.cpp`](file:///Users/integzz/Documents/GitHub/qflock/clockwidget.cpp) - Updated to use XML configuration
- [`flock.pro`](file:///Users/integzz/Documents/GitHub/qflock/flock.pro) - Added new source files
- [`CMakeLists.txt`](file:///Users/integzz/Documents/GitHub/qflock/CMakeLists.txt) - Added new source files
## XML Configuration Format
The configuration is stored in XML format with the following structure:
```xml
<?xml version="1.0" encoding="UTF-8"?>
<QFlockConfig>
<FontColor>#ffffff</FontColor>
<BackgroundColor>#96000000</BackgroundColor>
<FontSize>24</FontSize>
<AlwaysOnTop>true</AlwaysOnTop>
<WindowPosition>100,100</WindowPosition>
</QFlockConfig>
```
## Configuration Options
- **FontColor**: Font color in hex format (e.g., #ffffff for white)
- **BackgroundColor**: Background color in hex format with alpha (e.g., #96000000 for semi-transparent black)
- **FontSize**: Font size in points (8-72)
- **AlwaysOnTop**: Boolean value (true/false)
- **WindowPosition**: Window position as "x,y" coordinates
## Usage
The configuration is automatically loaded when the application starts and saved when it closes. Users can:
1. **Modify settings through the UI**: Right-click on the clock to access the context menu
2. **Edit config.xml directly**: The file is human-readable and can be edited manually
3. **Reset to defaults**: Use the "Reset Settings" option in the context menu
## Features
- ✅ XML-based configuration storage
- ✅ Automatic loading on startup
- ✅ Automatic saving on exit
- ✅ Default values if config file doesn't exist
- ✅ Human-readable XML format
- ✅ Error handling for corrupted config files
- ✅ Manual editing support
## Build and Test
Use the provided test script to build and verify the XML configuration:
```bash
./test_xml_config.sh
```
This will:
1. Clean previous build files
2. Build the project with XML configuration support
3. Display the current config.xml content
4. Provide instructions to run the application
The configuration file will be created automatically when you first run the application and make changes to the settings.
+4 -4
View File
@@ -12,18 +12,18 @@ rm -rf build/*
# Generate Makefile using qmake
echo "Generating Makefile..."
qmake flock.pro
qmake Clouck.pro
# Build project
echo "Starting compilation..."
make
# Check build result
if [ -f "build/flock.app/Contents/MacOS/flock" ] || [ -f "build/flock" ]; then
if [ -f "build/Clouck.app/Contents/MacOS/Clouck" ] || [ -f "build/Clouck" ]; then
echo "Build successful!"
echo "Executable location:"
[ -f "build/flock.app/Contents/MacOS/flock" ] && echo " - build/flock.app/Contents/MacOS/flock (macOS)"
[ -f "build/flock" ] && echo " - build/flock (Linux)"
[ -f "build/Clouck.app/Contents/MacOS/Clouck" ] && echo " - build/Clouck.app/Contents/MacOS/Clouck (macOS)"
[ -f "build/Clouck" ] && echo " - build/Clouck (Linux)"
else
echo "Build failed!"
exit 1
+67
View File
@@ -0,0 +1,67 @@
#!/bin/bash
# Cross-platform build script for Clouck
echo "Building Clouck - Cross-platform Clock Application"
echo "=================================================="
# Detect operating system
if [[ "$OSTYPE" == "darwin"* ]]; then
OS="macOS"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
OS="Linux"
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "win32" ]]; then
OS="Windows"
else
echo "Unsupported operating system: $OSTYPE"
exit 1
fi
echo "Detected OS: $OS"
echo ""
# Create build directory
mkdir -p build
# Clean old build files
echo "Cleaning old build files..."
rm -rf build/*
# Build using CMake (cross-platform)
echo "Configuring with CMake..."
cmake -B build -S . -DCMAKE_BUILD_TYPE=Release
if [ $? -ne 0 ]; then
echo "CMake configuration failed!"
exit 1
fi
echo "Building project..."
cmake --build build --config Release
if [ $? -ne 0 ]; then
echo "Build failed!"
exit 1
fi
echo ""
echo "Build successful!"
echo ""
echo "Executable location:"
if [ "$OS" == "macOS" ]; then
echo " - build/Clouck.app/Contents/MacOS/Clouck (macOS)"
elif [ "$OS" == "Windows" ]; then
echo " - build/Release/Clouck.exe (Windows)"
elif [ "$OS" == "Linux" ]; then
echo " - build/Clouck (Linux)"
fi
echo ""
echo "To run the application:"
if [ "$OS" == "macOS" ]; then
echo " ./build/Clouck.app/Contents/MacOS/Clouck"
elif [ "$OS" == "Windows" ]; then
echo " ./build/Release/Clouck.exe"
elif [ "$OS" == "Linux" ]; then
echo " ./build/Clouck"
fi
+3 -3
View File
@@ -10,9 +10,9 @@ if [ -d "build" ]; then
fi
# Delete .app bundle
if [ -d "flock.app" ]; then
rm -rf flock.app
echo "flock.app deleted"
if [ -d "Clouck.app" ]; then
rm -rf Clouck.app
echo "Clouck.app deleted"
fi
echo "Cleanup complete!"
+104 -10
View File
@@ -1,23 +1,36 @@
#include "clockwidget.h"
#include "configmanager.h"
#include "clock_widget.h"
#include "config_manager.h"
#include "window_helper.h"
#include <QVBoxLayout>
#include <QTime>
#include <QColorDialog>
#include <QFontDialog>
#include <QInputDialog>
#include <QMessageBox>
#include <QApplication>
ClockWidget::ClockWidget(QWidget *parent)
: QWidget(parent), m_dragging(false)
: QWidget(parent), m_dragging(false), m_resizing(false), m_resizeBorder(5)
{
setupUI();
createMenu();
loadSettings();
// Set window properties
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Tool);
// Set window properties for cross-platform always-on-top
Qt::WindowFlags flags = Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Tool;
#ifdef Q_OS_MAC
// On macOS, we need additional flags for proper always-on-top behavior in fullscreen
flags |= Qt::WindowDoesNotAcceptFocus;
setAttribute(Qt::WA_ShowWithoutActivating);
#endif
setWindowFlags(flags);
setAttribute(Qt::WA_TranslucentBackground);
setFixedSize(200, 80);
setMouseTracking(true); // Enable mouse tracking for resize cursor
// Set up platform-specific window properties after window creation
WindowHelper::setupAlwaysOnTopForFullscreen(this);
// Start timer
m_timer = new QTimer(this);
@@ -29,7 +42,7 @@ ClockWidget::ClockWidget(QWidget *parent)
ClockWidget::~ClockWidget()
{
saveSettings();
// Settings are now saved immediately after changes, no need to save here
}
void ClockWidget::setupUI()
@@ -79,20 +92,83 @@ void ClockWidget::createMenu()
void ClockWidget::mousePressEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
if (isInResizeArea(event->pos()))
{
m_resizing = true;
m_resizeStartPos = event->globalPosition().toPoint();
m_resizeStartSize = size();
event->accept();
}
else
{
m_dragging = true;
m_dragPosition = event->globalPosition().toPoint() - frameGeometry().topLeft();
event->accept();
}
}
}
void ClockWidget::mouseMoveEvent(QMouseEvent *event)
{
if (m_dragging && (event->buttons() & Qt::LeftButton))
if (m_resizing && (event->buttons() & Qt::LeftButton))
{
QPoint delta = event->globalPosition().toPoint() - m_resizeStartPos;
QSize newSize = m_resizeStartSize + QSize(delta.x(), delta.y());
// Minimum size constraints
newSize = newSize.expandedTo(QSize(100, 50));
resize(newSize);
event->accept();
}
else if (m_dragging && (event->buttons() & Qt::LeftButton))
{
move(event->globalPosition().toPoint() - m_dragPosition);
event->accept();
}
else
{
// Update cursor shape when hovering over resize area
if (isInResizeArea(event->pos()))
{
setCursor(Qt::SizeFDiagCursor);
}
else
{
setCursor(Qt::ArrowCursor);
}
}
}
void ClockWidget::mouseReleaseEvent(QMouseEvent *event)
{
if (event->button() == Qt::LeftButton)
{
if (m_resizing)
{
m_resizing = false;
// Save the new size and position to config
if (m_configManager)
{
m_configManager->setWindowPosition(pos());
m_configManager->setWindowSize(size());
m_configManager->saveSettings();
}
event->accept();
}
else if (m_dragging)
{
m_dragging = false;
// Save the new position to config
if (m_configManager)
{
m_configManager->setWindowPosition(pos());
m_configManager->saveSettings();
}
event->accept();
}
}
}
void ClockWidget::contextMenuEvent(QContextMenuEvent *event)
@@ -121,6 +197,12 @@ void ClockWidget::toggleAlwaysOnTop()
}
setWindowFlags(flags);
show();
// Re-apply platform-specific window properties after changing window flags
if (m_alwaysOnTop)
{
WindowHelper::setupAlwaysOnTopForFullscreen(this);
}
}
void ClockWidget::changeFontColor()
@@ -165,6 +247,8 @@ void ClockWidget::resetSettings()
m_configManager->resetToDefaults();
loadSettings();
updateStyleSheet();
// Reset to default size
resize(m_configManager->windowSize());
}
}
@@ -173,6 +257,14 @@ void ClockWidget::quitApplication()
qApp->quit();
}
bool ClockWidget::isInResizeArea(const QPoint &pos) const
{
// Check if mouse is in the bottom-right corner resize area
QRect resizeArea(rect().bottomRight() - QPoint(m_resizeBorder * 2, m_resizeBorder * 2),
QSize(m_resizeBorder * 2, m_resizeBorder * 2));
return resizeArea.contains(pos);
}
void ClockWidget::loadSettings()
{
m_configManager = new ConfigManager(this);
@@ -190,14 +282,15 @@ void ClockWidget::loadSettings()
m_fontSize = m_configManager->fontSize();
m_alwaysOnTop = m_configManager->alwaysOnTop();
// Restore window position
// Restore window position and size
move(m_configManager->windowPosition());
resize(m_configManager->windowSize());
// Update always on top checkbox in menu
QList<QAction *> actions = m_contextMenu->actions();
for (QAction *action : actions)
{
if (action->text() == "Always on Top")
if (action->text() == QString("Always on Top"))
{
action->setChecked(m_alwaysOnTop);
break;
@@ -218,6 +311,7 @@ void ClockWidget::saveSettings()
m_configManager->setFontSize(m_fontSize);
m_configManager->setAlwaysOnTop(m_alwaysOnTop);
m_configManager->setWindowPosition(pos());
m_configManager->setWindowSize(size());
// Save to XML file
if (!m_configManager->saveSettings())
+17 -3
View File
@@ -1,5 +1,5 @@
#ifndef CLOCKWIDGET_H
#define CLOCKWIDGET_H
#ifndef CLOCK_WIDGET_H
#define CLOCK_WIDGET_H
#include <QWidget>
#include <QLabel>
@@ -22,6 +22,7 @@ public:
protected:
void mousePressEvent(QMouseEvent *event) override;
void mouseMoveEvent(QMouseEvent *event) override;
void mouseReleaseEvent(QMouseEvent *event) override;
void contextMenuEvent(QContextMenuEvent *event) override;
private slots:
@@ -48,12 +49,25 @@ private:
QPoint m_dragPosition;
bool m_dragging;
// Resize related
bool m_resizing;
QPoint m_resizeStartPos;
QSize m_resizeStartSize;
int m_resizeBorder;
// Settings
ConfigManager *m_configManager;
QColor m_fontColor;
QColor m_backgroundColor;
int m_fontSize;
bool m_alwaysOnTop;
// Helper methods
bool isInResizeArea(const QPoint &pos) const;
#ifdef Q_OS_MAC
void setupMacOSWindowProperties();
#endif
};
#endif // CLOCKWIDGET_H
#endif // CLOCK_WIDGET_H
+3 -2
View File
@@ -1,8 +1,9 @@
<?xml version="1.0" encoding="UTF-8"?>
<QFlockConfig>
<FontColor>#ffffff</FontColor>
<FontColor>#ffffffff</FontColor>
<BackgroundColor>#96000000</BackgroundColor>
<FontSize>24</FontSize>
<AlwaysOnTop>true</AlwaysOnTop>
<WindowPosition>100,100</WindowPosition>
<WindowPosition>1132,708</WindowPosition>
<WindowSize>137,59</WindowSize>
</QFlockConfig>
+21 -3
View File
@@ -1,4 +1,4 @@
#include "configmanager.h"
#include "config_manager.h"
#include <QFile>
#include <QDebug>
@@ -26,7 +26,7 @@ bool ConfigManager::loadSettings(const QString &filename)
QXmlStreamReader reader(&file);
// Check if it's a valid XML file
if (!reader.readNextStartElement() || reader.name() != "QFlockConfig")
if (!reader.readNextStartElement() || reader.name() != QString("QFlockConfig"))
{
qDebug() << "Invalid config file format";
file.close();
@@ -53,7 +53,7 @@ bool ConfigManager::loadSettings(const QString &filename)
}
else if (name == "AlwaysOnTop")
{
m_alwaysOnTop = (text.toLower() == "true");
m_alwaysOnTop = (text.toLower() == QString("true"));
}
else if (name == "WindowPosition")
{
@@ -66,6 +66,17 @@ bool ConfigManager::loadSettings(const QString &filename)
m_windowPosition = QPoint(x, y);
}
}
else if (name == "WindowSize")
{
// Parse size format: "width,height"
QStringList dims = text.split(',');
if (dims.size() == 2)
{
int width = dims[0].toInt();
int height = dims[1].toInt();
m_windowSize = QSize(width, height);
}
}
else
{
reader.skipCurrentElement();
@@ -117,6 +128,12 @@ bool ConfigManager::saveSettings(const QString &filename)
.arg(m_windowPosition.y());
writer.writeTextElement("WindowPosition", positionStr);
// Window size
QString sizeStr = QString("%1,%2")
.arg(m_windowSize.width())
.arg(m_windowSize.height());
writer.writeTextElement("WindowSize", sizeStr);
// Close root element
writer.writeEndElement();
@@ -143,4 +160,5 @@ void ConfigManager::setDefaultValues()
m_fontSize = 24;
m_alwaysOnTop = true;
m_windowPosition = QPoint(100, 100);
m_windowSize = QSize(200, 80); // Default clock size
}
+7 -3
View File
@@ -1,9 +1,10 @@
#ifndef CONFIGMANAGER_H
#define CONFIGMANAGER_H
#ifndef CONFIG_MANAGER_H
#define CONFIG_MANAGER_H
#include <QObject>
#include <QColor>
#include <QPoint>
#include <QSize>
#include <QString>
#include <QXmlStreamReader>
#include <QXmlStreamWriter>
@@ -27,6 +28,7 @@ public:
int fontSize() const { return m_fontSize; }
bool alwaysOnTop() const { return m_alwaysOnTop; }
QPoint windowPosition() const { return m_windowPosition; }
QSize windowSize() const { return m_windowSize; }
// Setters
void setFontColor(const QColor &color) { m_fontColor = color; }
@@ -34,6 +36,7 @@ public:
void setFontSize(int size) { m_fontSize = size; }
void setAlwaysOnTop(bool onTop) { m_alwaysOnTop = onTop; }
void setWindowPosition(const QPoint &pos) { m_windowPosition = pos; }
void setWindowSize(const QSize &size) { m_windowSize = size; }
// Reset to default values
void resetToDefaults();
@@ -51,6 +54,7 @@ private:
int m_fontSize;
bool m_alwaysOnTop;
QPoint m_windowPosition;
QSize m_windowSize;
};
#endif // CONFIGMANAGER_H
#endif // CONFIG_MANAGER_H
BIN
View File
Binary file not shown.

Before

Width:  |  Height:  |  Size: 72 KiB

After

Width:  |  Height:  |  Size: 50 KiB

-27
View File
@@ -1,27 +0,0 @@
# Created by and for Qt Creator This file was created for editing the project sources only.
# You may attempt to use it for building too, by modifying this file here.
TARGET = flock
QT = core gui widgets
SOURCES = \
$$PWD/main.cpp \
$$PWD/clockwidget.cpp \
$$PWD/configmanager.cpp
HEADERS = \
$$PWD/clockwidget.h \
$$PWD/configmanager.h
INCLUDEPATH = \
$$PWD/.
# Set build output directories
DESTDIR = $$PWD/build
OBJECTS_DIR = $$PWD/build
MOC_DIR = $$PWD/build
RCC_DIR = $$PWD/build
UI_DIR = $$PWD/build
#DEFINES =
+1 -1
View File
@@ -1,5 +1,5 @@
#include <QApplication>
#include "clockwidget.h"
#include "clock_widget.h"
int main(int argc, char *argv[])
{
+27
View File
@@ -0,0 +1,27 @@
#!/bin/bash
echo "QFlock Clock Application - macOS Fullscreen Always-on-Top Feature Test"
echo "======================================================================"
echo ""
echo "[SUCCESS] Compilation successful! Application build completed"
echo "[SUCCESS] macOS fullscreen always-on-top feature integrated"
echo ""
echo "Feature highlights:"
echo "• Basic always-on-top using Qt::WindowStaysOnTopHint"
echo "• Native macOS window level with NSWindowLevel"
echo "• Support for all desktop spaces (NSWindowCollectionBehaviorCanJoinAllSpaces)"
echo "• Fullscreen mode support (NSWindowCollectionBehaviorFullScreenAuxiliary)"
echo "• Prevents window focus stealing (Qt::WindowDoesNotAcceptFocus)"
echo ""
echo "Testing method:"
echo "1. Run ./build/Clouck.app/Contents/MacOS/Clouck"
echo "2. Open Safari or other apps in fullscreen mode"
echo "3. Observe if clock stays on top layer"
echo ""
echo "Optimized file structure:"
echo "• clock_widget.h/cpp - Main clock component"
echo "• config_manager.h/cpp - Configuration management"
echo "• window_helper_macos.h/mm - macOS native window helper"
echo ""
echo "Press any key to exit..."
read -n 1 -s
+39
View File
@@ -0,0 +1,39 @@
#include <QCoreApplication>
#include <QDebug>
#include <QFile>
#include <QSize>
#include <QPoint>
#include "config_manager.h"
int main(int argc, char *argv[])
{
QCoreApplication app(argc, argv);
ConfigManager configManager;
// Set some test values
configManager.setWindowPosition(QPoint(100, 200));
configManager.setWindowSize(QSize(300, 400));
// Save to file
if (configManager.saveSettings("test_config.xml"))
{
qDebug() << "Config saved successfully";
// Read the file content
QFile file("test_config.xml");
if (file.open(QIODevice::ReadOnly | QIODevice::Text))
{
QString content = file.readAll();
qDebug() << "File content:";
qDebug() << content;
file.close();
}
}
else
{
qDebug() << "Failed to save config";
}
return 0;
}
+128
View File
@@ -0,0 +1,128 @@
#!/bin/bash
# Cross-platform test script for Clouck
echo "Clouck Cross-Platform Test Suite"
echo "================================"
echo ""
# Detect operating system
if [[ "$OSTYPE" == "darwin"* ]]; then
OS="macOS"
EXECUTABLE="./build/Clouck"
elif [[ "$OSTYPE" == "linux-gnu"* ]]; then
OS="Linux"
EXECUTABLE="./build/Clouck"
elif [[ "$OSTYPE" == "msys" ]] || [[ "$OSTYPE" == "cygwin" ]] || [[ "$OSTYPE" == "win32" ]]; then
OS="Windows"
EXECUTABLE="./build/Clouck"
else
echo "Unsupported operating system: $OSTYPE"
exit 1
fi
echo "Detected OS: $OS"
echo "Executable: $EXECUTABLE"
echo ""
# Check if executable exists
if [ ! -f "$EXECUTABLE" ]; then
echo "[ERROR] Executable not found: $EXECUTABLE"
echo "Please build the project first using: ./build_cross_platform.sh"
exit 1
fi
echo "[SUCCESS] Executable found"
echo ""
# Test 1: Basic functionality
echo "Test 1: Basic Clock Functionality"
echo "---------------------------------"
echo "• Starting Clouck application..."
echo "• Application should display a clock widget"
echo "• Clock should update every second"
echo ""
# Start the application in background
$EXECUTABLE &
PID=$!
sleep 3
# Check if application is running
if ps -p $PID >/dev/null; then
echo "[SUCCESS] Application started successfully (PID: $PID)"
else
echo "[ERROR] Application failed to start"
exit 1
fi
echo ""
echo "Test 2: Always-on-Top Feature"
echo "------------------------------"
if [ "$OS" == "macOS" ]; then
echo "• Open Safari or any other application"
echo "• Enter fullscreen mode (Control + Command + F)"
echo "• Clock should remain visible on top"
elif [ "$OS" == "Windows" ]; then
echo "• Open any application in fullscreen mode"
echo "• Clock should remain visible on top"
elif [ "$OS" == "Linux" ]; then
echo "• Switch to different workspace"
echo "• Clock should appear on all workspaces"
fi
echo ""
echo "Test 3: Resizable Feature"
echo "-------------------------"
echo "• Move mouse to bottom-right corner of clock"
echo "• Cursor should change to diagonal resize arrow"
echo "• Click and drag to resize"
echo "• Release mouse to save new size"
echo ""
echo "Test 4: Context Menu"
echo "---------------------"
echo "• Right-click on clock to show context menu"
echo "• Test various menu options:"
echo " - Change color"
echo " - Change font"
echo " - Toggle always on top"
echo " - Toggle click through"
echo " - Quit application"
echo ""
echo "Platform-Specific Features:"
echo "---------------------------"
if [ "$OS" == "macOS" ]; then
echo "• Native macOS window level integration"
echo "• Support for all desktop spaces"
echo "• Fullscreen mode compatibility"
elif [ "$OS" == "Windows" ]; then
echo "• Windows native window management"
echo "• Taskbar exclusion"
echo "• Click-through support"
elif [ "$OS" == "Linux" ]; then
echo "• X11 window manager integration"
echo "• Workspace stickiness"
echo "• EWMH (Extended Window Manager Hints) support"
fi
echo ""
echo "To stop testing:"
echo "• Right-click on clock and select 'Quit'"
echo "• Or run: kill $PID"
echo ""
echo "Test the features now! Press any key when finished..."
read -n 1 -s
echo ""
echo "Stopping application..."
kill $PID 2>/dev/null
sleep 1
echo ""
echo "Test completed!"
echo ""
echo "Configuration file location:"
echo "• macOS: ~/Library/Preferences/Clouck/config.xml"
echo "• Windows: %APPDATA%\\Clouck\\config.xml"
echo "• Linux: ~/.config/Clouck/config.xml"
+15
View File
@@ -0,0 +1,15 @@
#!/bin/bash
echo "Testing QFlock Clock Application Fullscreen Always-on-Top Feature..."
echo ""
echo "Test Steps:"
echo "1. Clock application should already be running"
echo "2. Open any application (like Safari) and enter fullscreen mode"
echo "3. Observe if the clock still displays above the fullscreen application"
echo "4. You can use Command+Tab to switch applications, the clock should remain visible"
echo ""
echo "Press any key to exit test instructions..."
read -n 1 -s
echo ""
echo "Test complete! If the clock remains visible in fullscreen mode, the feature is working correctly."
+80
View File
@@ -0,0 +1,80 @@
#!/bin/bash
# GitHub Actions Local Testing Script
# This script helps test GitHub Actions workflows locally using act
echo "GitHub Actions Local Testing for Clouck"
echo "========================================"
# Check if act is installed
if ! command -v act &> /dev/null; then
echo "❌ act is not installed. Installing..."
echo "Please install act from: https://github.com/nektos/act"
echo "On macOS: brew install act"
echo "On Linux: curl https://raw.githubusercontent.com/nektos/act/master/install.sh | bash"
exit 1
fi
# Display available workflows
echo "Available workflows:"
echo "1. CI Build (ci.yml) - Quick validation builds"
echo "2. Build Qt (build.yml) - Comprehensive builds"
echo "3. Release (release.yml) - Create releases"
echo "4. All workflows"
echo ""
# Function to run workflow
run_workflow() {
local workflow=$1
local event=$2
echo "Running $workflow with event: $event"
act -W .github/workflows/$workflow -e $event --dry-run
}
# Menu
read -p "Which workflow do you want to test? (1-4): " choice
case $choice in
1)
echo "Testing CI Build workflow..."
run_workflow "ci.yml" "push"
;;
2)
echo "Testing Build Qt workflow..."
run_workflow "build.yml" "push"
;;
3)
echo "Testing Release workflow..."
# Create a mock release event
cat > /tmp/release-event.json << EOF
{
"ref": "refs/tags/v1.0.0",
"repository": {
"name": "floating-clock",
"owner": {
"login": "ivaquero"
}
}
}
EOF
run_workflow "release.yml" "/tmp/release-event.json"
;;
4)
echo "Testing all workflows..."
act --dry-run
;;
*)
echo "Invalid choice. Exiting."
exit 1
;;
esac
echo ""
echo "To run the workflows for real (not dry-run), remove the --dry-run flag"
echo "Example: act -W .github/workflows/ci.yml"
echo ""
echo "For local testing with Docker, you may need to use different images:"
echo "act -P ubuntu-latest=nektos/act-environments-ubuntu:18.04"
echo "act -P macos-latest=nektos/act-environments-macos:latest"
echo "act -P windows-latest=nektos/act-environments-windows:latest"
+57
View File
@@ -0,0 +1,57 @@
#!/bin/bash
echo "Testing QFlock Resizable Feature"
echo "================================"
echo ""
echo "1. Starting QFlock application..."
./build/Clouck.app/Contents/MacOS/Clouck &
PID=$!
sleep 2
echo ""
echo "2. Checking if application is running..."
if ps -p $PID >/dev/null; then
echo "[SUCCESS] Application started successfully (PID: $PID)"
else
echo "[ERROR] Application failed to start"
exit 1
fi
echo ""
echo "3. Checking configuration file..."
if [ -f "config.xml" ]; then
echo "[INFO] Configuration file exists"
echo "Current configuration:"
cat config.xml
else
echo "[WARNING] Configuration file not found"
fi
echo ""
echo "4. Instructions for testing resizable feature:"
echo " - Move mouse to bottom-right corner of the clock"
echo " - Cursor should change to diagonal resize arrow"
echo " - Click and drag to resize"
echo " - Release mouse to save new size"
echo " - Size will be saved to config.xml"
echo ""
echo "5. To stop the application:"
echo " - Right-click on clock and select 'Quit'"
echo " - Or run: kill $PID"
echo ""
echo "Test the resizable feature now!"
echo "Press any key to continue..."
read -n 1 -s
echo ""
echo "6. Checking final configuration..."
if [ -f "config.xml" ]; then
echo "Final configuration:"
cat config.xml
else
echo "Configuration file not found"
fi
echo ""
echo "Test completed!"
+95
View File
@@ -0,0 +1,95 @@
#include "window_helper.h"
#ifdef Q_OS_MAC
#include "window_helper_macos.h"
#elif defined(Q_OS_WIN)
#include "window_helper_windows.h"
#elif defined(Q_OS_LINUX)
#include "window_helper_linux.h"
#endif
void WindowHelper::setupAlwaysOnTopForFullscreen(QWidget *widget)
{
if (!widget)
return;
#ifdef Q_OS_MAC
setupMacOS(widget);
#elif defined(Q_OS_WIN)
setupWindows(widget);
#elif defined(Q_OS_LINUX)
setupLinux(widget);
#endif
}
void WindowHelper::setWindowAlwaysOnTop(QWidget *widget, bool alwaysOnTop)
{
if (!widget)
return;
Qt::WindowFlags flags = widget->windowFlags();
if (alwaysOnTop)
{
flags |= Qt::WindowStaysOnTopHint;
}
else
{
flags &= ~Qt::WindowStaysOnTopHint;
}
widget->setWindowFlags(flags);
widget->show(); // Required to apply new flags
}
void WindowHelper::setWindowClickThrough(QWidget *widget, bool clickThrough)
{
if (!widget)
return;
#ifdef Q_OS_MAC
// macOS: Make window ignore mouse events
widget->setAttribute(Qt::WA_TransparentForMouseEvents, clickThrough);
#elif defined(Q_OS_WIN)
// Windows: Will be implemented in Windows helper
widget->setAttribute(Qt::WA_TransparentForMouseEvents, clickThrough);
#elif defined(Q_OS_LINUX)
// Linux: Will be implemented in Linux helper
widget->setAttribute(Qt::WA_TransparentForMouseEvents, clickThrough);
#endif
}
void WindowHelper::setWindowTransparent(QWidget *widget, bool transparent)
{
if (!widget)
return;
if (transparent)
{
widget->setAttribute(Qt::WA_TranslucentBackground);
widget->setWindowFlags(widget->windowFlags() | Qt::FramelessWindowHint);
}
else
{
widget->setAttribute(Qt::WA_TranslucentBackground, false);
}
}
#ifdef Q_OS_MAC
void WindowHelper::setupMacOS(QWidget *widget)
{
MacOSWindowHelper::setupAlwaysOnTopForFullscreen(widget);
}
#endif
#ifdef Q_OS_WIN
void WindowHelper::setupWindows(QWidget *widget)
{
WindowHelperWindows::setupAlwaysOnTopForFullscreen(widget);
}
#endif
#ifdef Q_OS_LINUX
void WindowHelper::setupLinux(QWidget *widget)
{
WindowHelperLinux::setupAlwaysOnTopForFullscreen(widget);
}
#endif
+21
View File
@@ -0,0 +1,21 @@
#ifndef WINDOW_HELPER_H
#define WINDOW_HELPER_H
#include <QWidget>
class WindowHelper
{
public:
static void setupAlwaysOnTopForFullscreen(QWidget *widget);
static void setWindowAlwaysOnTop(QWidget *widget, bool alwaysOnTop);
static void setWindowClickThrough(QWidget *widget, bool clickThrough);
static void setWindowTransparent(QWidget *widget, bool transparent);
private:
// Platform-specific implementations
static void setupMacOS(QWidget *widget);
static void setupWindows(QWidget *widget);
static void setupLinux(QWidget *widget);
};
#endif // WINDOW_HELPER_H
+114
View File
@@ -0,0 +1,114 @@
#include "window_helper_linux.h"
#ifdef Q_OS_LINUX
#include <QWidget>
#include <QGuiApplication>
#include <qpa/qplatformnativeinterface.h>
#include <X11/Xlib.h>
#include <X11/Xatom.h>
#include <X11/extensions/Xfixes.h>
void WindowHelperLinux::setupAlwaysOnTopForFullscreen(QWidget *widget)
{
if (!widget)
return;
// Get X11 display from Qt platform interface
QNativeInterface::QX11Application *x11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
if (!x11App)
return;
Display *display = x11App->display();
if (!display)
return;
WId windowId = widget->winId();
if (!windowId)
return;
// Set window type to utility (similar to macOS panel)
Atom windowType = XInternAtom(display, "_NET_WM_WINDOW_TYPE", False);
Atom windowTypeUtility = XInternAtom(display, "_NET_WM_WINDOW_TYPE_UTILITY", False);
XChangeProperty(display, windowId, windowType, XA_ATOM, 32,
PropModeReplace, (unsigned char *)&windowTypeUtility, 1);
// Set window state to above
Atom windowState = XInternAtom(display, "_NET_WM_STATE", False);
Atom windowStateAbove = XInternAtom(display, "_NET_WM_STATE_ABOVE", False);
XChangeProperty(display, windowId, windowState, XA_ATOM, 32,
PropModeReplace, (unsigned char *)&windowStateAbove, 1);
// Set window state to skip taskbar
Atom windowStateSkipTaskbar = XInternAtom(display, "_NET_WM_STATE_SKIP_TASKBAR", False);
XChangeProperty(display, windowId, windowState, XA_ATOM, 32,
PropModeReplace, (unsigned char *)&windowStateSkipTaskbar, 1);
// Set window state to skip pager
Atom windowStateSkipPager = XInternAtom(display, "_NET_WM_STATE_SKIP_PAGER", False);
XChangeProperty(display, windowId, windowState, XA_ATOM, 32,
PropModeReplace, (unsigned char *)&windowStateSkipPager, 1);
// Set window state to sticky (appear on all workspaces)
Atom windowStateSticky = XInternAtom(display, "_NET_WM_STATE_STICKY", False);
XChangeProperty(display, windowId, windowState, XA_ATOM, 32,
PropModeReplace, (unsigned char *)&windowStateSticky, 1);
}
void WindowHelperLinux::setWindowAlwaysOnTop(QWidget *widget, bool alwaysOnTop)
{
if (!widget)
return;
Qt::WindowFlags flags = widget->windowFlags();
if (alwaysOnTop)
{
flags |= Qt::WindowStaysOnTopHint;
}
else
{
flags &= ~Qt::WindowStaysOnTopHint;
}
widget->setWindowFlags(flags);
widget->show(); // Required to apply new flags
}
void WindowHelperLinux::setWindowClickThrough(QWidget *widget, bool clickThrough)
{
if (!widget)
return;
// Get X11 display from Qt platform interface
QNativeInterface::QX11Application *x11App = qApp->nativeInterface<QNativeInterface::QX11Application>();
if (!x11App)
return;
Display *display = x11App->display();
if (!display)
return;
WId windowId = widget->winId();
if (!windowId)
return;
if (clickThrough)
{
// Set window input shape to empty region
XRectangle rect = {0, 0, 0, 0};
XFixesSetWindowShapeRegion(display, windowId, ShapeInput, 0, 0, None);
XFixesSetWindowShapeRegion(display, windowId, ShapeInput, 0, 0,
XFixesCreateRegion(display, &rect, 1));
}
else
{
// Reset window input shape
XFixesSetWindowShapeRegion(display, windowId, ShapeInput, 0, 0, None);
}
}
void WindowHelperLinux::setupLinux(QWidget *widget)
{
setupAlwaysOnTopForFullscreen(widget);
}
#endif // Q_OS_LINUX
+15
View File
@@ -0,0 +1,15 @@
#ifndef WINDOW_HELPER_LINUX_H
#define WINDOW_HELPER_LINUX_H
#include <QWidget>
class WindowHelperLinux
{
public:
static void setupAlwaysOnTopForFullscreen(QWidget *widget);
static void setWindowAlwaysOnTop(QWidget *widget, bool alwaysOnTop);
static void setWindowClickThrough(QWidget *widget, bool clickThrough);
static void setupLinux(QWidget *widget);
};
#endif // WINDOW_HELPER_LINUX_H
+16
View File
@@ -0,0 +1,16 @@
#ifndef WINDOW_HELPER_MACOS_H
#define WINDOW_HELPER_MACOS_H
class QWidget;
#ifdef __OBJC__
@class NSWindow;
#endif
class MacOSWindowHelper
{
public:
static void setupAlwaysOnTopForFullscreen(QWidget *widget);
};
#endif // WINDOW_HELPER_MACOS_H
+36
View File
@@ -0,0 +1,36 @@
#import "window_helper_macos.h"
#import <QWidget>
#ifdef Q_OS_MAC
#import <AppKit/AppKit.h>
#import <CoreGraphics/CGWindowLevel.h>
void MacOSWindowHelper::setupAlwaysOnTopForFullscreen(QWidget *widget)
{
if (!widget) return;
// Get the native NSView and NSWindow
NSView *nativeView = (NSView *)widget->winId();
NSWindow *nativeWindow = [nativeView window];
if (nativeWindow) {
// Set window level to be above everything, including fullscreen apps
[nativeWindow setLevel:NSScreenSaverWindowLevel - 1];
// Allow window to appear on all spaces (including fullscreen spaces)
[nativeWindow setCollectionBehavior:NSWindowCollectionBehaviorCanJoinAllSpaces |
NSWindowCollectionBehaviorFullScreenAuxiliary |
NSWindowCollectionBehaviorStationary];
// Make it a floating panel that won't steal focus
[nativeWindow setHidesOnDeactivate:NO];
// Note: setFloatingPanel is deprecated, but we keep it for compatibility
// The key is setting the proper window level and collection behavior
// Ensure it stays above dock and menu bar
[nativeWindow setLevel:CGWindowLevelForKey(kCGFloatingWindowLevelKey)];
}
}
#endif // Q_OS_MAC
+122
View File
@@ -0,0 +1,122 @@
#include "window_helper_windows.h"
#ifdef Q_OS_WIN
#include <QWidget>
#include <QWindow>
#include <qt_windows.h>
#include <windows.h>
// Ensure we have the proper Windows version for API functions
#ifndef _WIN32_WINNT
#define _WIN32_WINNT 0x0600 // Windows Vista and later
#endif
void WindowHelperWindows::setupAlwaysOnTopForFullscreen(QWidget *widget)
{
if (!widget)
return;
HWND hwnd = getWindowHandle(widget);
if (!hwnd)
return;
// Set window as topmost
SetWindowPos(hwnd, HWND_TOPMOST, 0, 0, 0, 0,
SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
// Make window appear on all desktops (similar to macOS spaces)
SetWindowLongPtr(hwnd, GWL_EXSTYLE,
GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_TOOLWINDOW);
// Set extended window style to stay on top in fullscreen mode
SetWindowLongPtr(hwnd, GWL_EXSTYLE,
GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_TOPMOST);
// Make window not appear in taskbar
SetWindowLongPtr(hwnd, GWL_EXSTYLE,
GetWindowLongPtr(hwnd, GWL_EXSTYLE) | WS_EX_NOACTIVATE);
}
void WindowHelperWindows::setWindowAlwaysOnTop(QWidget *widget, bool alwaysOnTop)
{
if (!widget)
return;
HWND hwnd = getWindowHandle(widget);
if (!hwnd)
return;
SetWindowPos(hwnd, alwaysOnTop ? HWND_TOPMOST : HWND_NOTOPMOST,
0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_NOACTIVATE);
}
void WindowHelperWindows::setWindowClickThrough(QWidget *widget, bool clickThrough)
{
if (!widget)
return;
HWND hwnd = getWindowHandle(widget);
if (!hwnd)
return;
if (clickThrough)
{
// Make window transparent to mouse events
SetWindowLong(hwnd, GWL_EXSTYLE,
GetWindowLong(hwnd, GWL_EXSTYLE) | WS_EX_TRANSPARENT);
}
else
{
// Remove transparent mouse event handling
SetWindowLongPtr(hwnd, GWL_EXSTYLE,
GetWindowLongPtr(hwnd, GWL_EXSTYLE) & ~WS_EX_TRANSPARENT);
}
}
void WindowHelperWindows::setWindowExStyle(QWidget *widget, DWORD style, bool enable)
{
if (!widget)
return;
HWND hwnd = getWindowHandle(widget);
if (!hwnd)
return;
DWORD currentStyle = GetWindowLongPtr(hwnd, GWL_EXSTYLE);
if (enable)
{
currentStyle |= style;
}
else
{
currentStyle &= ~style;
}
SetWindowLongPtr(hwnd, GWL_EXSTYLE, currentStyle);
}
HWND WindowHelperWindows::getWindowHandle(QWidget *widget)
{
if (!widget)
return nullptr;
QWindow *window = widget->windowHandle();
if (!window)
{
// Try to get window handle from native parent
QWidget *nativeParent = widget->nativeParentWidget();
if (nativeParent)
{
window = nativeParent->windowHandle();
}
}
if (window)
{
return reinterpret_cast<HWND>(window->winId());
}
return nullptr;
}
#endif // Q_OS_WIN
+22
View File
@@ -0,0 +1,22 @@
#ifndef WINDOW_HELPER_WINDOWS_H
#define WINDOW_HELPER_WINDOWS_H
#include <QWidget>
#ifdef Q_OS_WIN
#include <Windows.h>
#endif
class WindowHelperWindows
{
public:
static void setupAlwaysOnTopForFullscreen(QWidget *widget);
static void setWindowAlwaysOnTop(QWidget *widget, bool alwaysOnTop);
static void setWindowClickThrough(QWidget *widget, bool clickThrough);
private:
static void setWindowExStyle(QWidget *widget, DWORD style, bool enable);
static HWND getWindowHandle(QWidget *widget);
};
#endif // WINDOW_HELPER_WINDOWS_H