diff --git a/CMakeLists.txt b/CMakeLists.txt index 512a72c..0a64cdc 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,60 +1,38 @@ -cmake_minimum_required(VERSION 3.7...3.21) - -if(${CMAKE_VERSION} VERSION_LESS 3.12) - cmake_policy(VERSION ${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION}) -endif() - -project(FloatingClock) +cmake_minimum_required(VERSION 3.16) +project(flock) set(CMAKE_CXX_STANDARD 17) set(CMAKE_CXX_STANDARD_REQUIRED ON) -set(CMAKE_AUTOUIC ON) -set(CMAKE_AUTOMOC ON) -set(CMAKE_AUTORCC ON) -set(CMAKE_CXX_STANDARD 17 CACHE STRING "v") -set(CMAKE_CXX_STANDARD_REQUIRED True) -set(CMAKE_CXX_EXTENSIONS OFF) -set(CMAKE_EXPORT_COMPILE_COMMANDS ON) +# Find Qt6 +find_package(Qt6 REQUIRED COMPONENTS Core Widgets) -# Homebrew Qt6 路径 (自动检测或手动设置) -if(CMAKE_SYSTEM_NAME STREQUAL "Darwin") - # 检测架构 - if(CMAKE_SYSTEM_PROCESSOR STREQUAL "arm64") - set(QT6_PATH "/opt/homebrew/opt/qt" CACHE PATH "Qt6 installation path") - else() - set(QT6_PATH "/usr/local/opt/qt" CACHE PATH "Qt6 installation path") - endif() +# Set up Qt6 +qt6_standard_project_setup() - # 添加到 CMake 搜索路径 - list(APPEND CMAKE_PREFIX_PATH "${QT6_PATH}/lib/cmake") -endif() - -find_package(Qt6 REQUIRED COMPONENTS Core Gui Widgets Xml) - -add_executable(${CMAKE_PROJECT_NAME} WIN32 MACOSX_BUNDLE main.cpp) - -set_target_properties(${CMAKE_PROJECT_NAME} PROPERTIES - WIN32_EXECUTABLE TRUE - MACOSX_BUNDLE TRUE +# Sources +set(SOURCES + main.cpp + clockwidget.cpp ) -target_link_libraries(${CMAKE_PROJECT_NAME} PRIVATE Qt6::Core - Qt6::Gui +set(HEADERS + clockwidget.h +) + +# Create executable +qt6_add_executable(flock + ${SOURCES} + ${HEADERS} +) + +# Link Qt libraries +target_link_libraries(flock + Qt6::Core Qt6::Widgets - Qt6::Xml ) -# macOS 特定设置 -if(APPLE) - set_target_properties(FloatingClock PROPERTIES - MACOSX_BUNDLE TRUE - MACOSX_BUNDLE_INFO_PLIST ${CMAKE_SOURCE_DIR}/Info.plist.in - ) - - # 修复运行时库路径 - set_target_properties(FloatingClock PROPERTIES - INSTALL_RPATH "${QT6_PATH}/lib" - BUILD_WITH_INSTALL_RPATH TRUE - ) -endif() +# Set up install +install(TARGETS flock + RUNTIME DESTINATION bin +) diff --git a/FloatingClock.h b/FloatingClock.h deleted file mode 100644 index 789ece8..0000000 --- a/FloatingClock.h +++ /dev/null @@ -1,78 +0,0 @@ -#ifndef FLOATINGCLOCK_H -#define FLOATINGCLOCK_H - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -class FloatingClock : public QWidget -{ - Q_OBJECT - -public: - explicit FloatingClock(QWidget *parent = nullptr); - ~FloatingClock(); - -protected: - void mousePressEvent(QMouseEvent *event) override; - void mouseMoveEvent(QMouseEvent *event) override; - void mouseReleaseEvent(QMouseEvent *event) override; - void paintEvent(QPaintEvent *) override; - -private slots: - void updateTime(); - void changeFontSize(); - void changeFontColor(); - void changeBgColor(); - void resetSettings(); - void quitApp(); - -private: - void showContextMenu(const QPoint &pos); - void updateStyle(); - void moveToCenter(); - void loadXmlConfig(); - void createDefaultXml(const QString &path); - void setDefaultValues(); - void applyXmlValues(const QMap &values); - bool parseBool(const QString &str); - void loadUserSettings(); - void saveUserSettings(); - QColor parseColor(const QString &str); - - QLabel *timeLabel; - - int windowWidth, windowHeight; - int windowPosX, windowPosY; - int borderRadius; - int borderWidth; - QColor borderColor; - - int fontSize; - QString fontFamily; - bool fontWeight; - bool fontItalic; - QColor fontColor; - - QColor bgColor; - QString timeFormat; - - int shadowBlurRadius; - int shadowOffsetX, shadowOffsetY; - QColor shadowColor; - - bool dragging = false; - QPoint dragPos; -}; - -#endif diff --git a/Info.plist.in b/Info.plist.in deleted file mode 100644 index 81ce9d5..0000000 --- a/Info.plist.in +++ /dev/null @@ -1,26 +0,0 @@ - - - - - CFBundleExecutable - ${MACOSX_BUNDLE_EXECUTABLE_NAME} - CFBundleIdentifier - com.example.FloatingClock - CFBundleName - FloatingClock - CFBundleVersion - 1.0 - CFBundleShortVersionString - 1.0 - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - APPL - CFBundleSignature - ???? - LSMinimumSystemVersion - 10.12 - NSHighResolutionCapable - - - diff --git a/README-CN.md b/README-CN.md new file mode 100644 index 0000000..944e27e --- /dev/null +++ b/README-CN.md @@ -0,0 +1,123 @@ +# QFlock - Qt6 浮动时钟 + +## 功能特性 + +✅ **已实现的功能:** + +- **无边框窗口** - 简洁的浮动时钟界面 +- **拖拽移动** - 鼠标左键拖拽移动窗口位置 +- **右键菜单** - 丰富的自定义选项 +- **总在最前** - 始终显示在其他窗口之上 +- **透明背景** - 支持半透明背景效果 +- **自定义样式** - 可调整字体颜色、背景颜色、字体大小 +- **设置持久化** - 自动保存和恢复用户设置 +- **跨平台支持** - 基于Qt6,支持macOS/Linux/Windows + +## 使用方法 + +### 基本操作 + +- **移动时钟**:鼠标左键点击并拖拽 +- **显示菜单**:右键点击时钟 +- **退出应用**:右键菜单选择"退出" + +### 自定义设置 + +- **字体颜色**:右键菜单 → 设置字体颜色 +- **背景颜色**:右键菜单 → 设置背景颜色 +- **字体大小**:右键菜单 → 设置字体大小 +- **总在最前**:右键菜单 → 勾选/取消"总在最前" +- **重置设置**:右键菜单 → 重置设置 + +### 构建和运行 + +#### 快速构建(推荐): + +```bash +# 一键构建 +./build.sh + +# 运行应用 +./build/flock.app/Contents/MacOS/flock # macOS +./build/flock # Linux +``` + +#### 手动构建: + +```bash +# 清理旧的构建文件 +./clean.sh + +# 生成Makefile +qmake flock.pro + +# 编译 +make + +# 运行 +./build/flock.app/Contents/MacOS/flock # macOS +./build/flock # Linux +``` + +#### 使用CMake构建: + +```bash +mkdir build && cd build +cmake .. +make +./flock +``` + +### 项目结构 + +```text +qflock/ +├── build/ # 构建输出目录 +│ ├── flock.app/ # macOS应用包 +│ ├── *.o # 目标文件 +│ ├── moc_*.cpp # Qt元对象编译器输出 +│ └── Makefile # 构建文件 +├── src/ # 源代码(建议) +├── build.sh # 一键构建脚本 +├── clean.sh # 清理脚本 +├── flock.pro # qmake项目文件 +├── CMakeLists.txt # CMake项目文件 +└── README.md +``` + +## 技术特点 + +- **Qt6框架**:使用最新的Qt6库 +- **透明窗口**:支持RGBA透明背景 +- **定时器更新**:每秒更新时钟显示 +- **设置管理**:使用QSettings持久化用户偏好 +- **事件处理**:自定义鼠标事件实现拖拽功能 + +## 开发说明 + +### 构建输出管理 + +- 所有编译生成的文件(.o, moc_*.cpp, Makefile等)都会输出到`build/`目录 +- 可执行文件位于`build/flock.app/Contents/MacOS/flock`(macOS)或`build/flock`(Linux) +- 使用`./clean.sh`可以快速清理所有构建文件 +- 使用`./build.sh`可以进行完整的一键构建 + +### 自定义构建配置 + +可以在`flock.pro`中修改以下配置: + +- `DESTDIR`:可执行文件输出目录 +- `OBJECTS_DIR`:目标文件输出目录 +- `MOC_DIR`:元对象编译器输出目录 + +## 更新日志 + +### v1.0.0 (2026-04-18) + +- ✨ 初始版本发布 +- ✨ 基础时钟功能 +- ✨ 拖拽移动支持 +- ✨ 右键菜单功能 +- ✨ 样式自定义 +- ✨ 设置持久化 +- ✨ 构建输出目录管理 diff --git a/README.md b/README.md index cb6370a..41ab8ed 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,9 @@ -# ⏰ Floating Clock +# ⏰ Flock ![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) -This project aims to build a cross-platform floating clock based on PySide6 & Qt6. +This project aims to build a cross-platform floating clock based on Qt6. ## Motivation @@ -12,28 +12,108 @@ When on macOS / Linux, the clock on the menu bar is too small to be noticeable, ![clock](doc/clock.png) -## Roadmap +## ✨ Features -- [ ] Main Window - - [x] Frameless +- [ ] **Main Window** + - [ ] Frameless - [x] Drag and Move - [x] Right Click Menu - - [x] Always On Top + - [ ] Always On Top - [ ] Show in Fullscreen Mode -- [x] Font - - [x] Set Color +- [x] **Style** - [x] Set Background Color - - [x] Change Size -- [x] Configuratin File - - [x] Read Settings - - [x] Write Settings - - [x] Reset Settings -- [ ] Time + - [x] Set Font Color + - [x] Set FontSize +- [ ] **Configuration File** (XML) + - [ ] Read Settings + - [ ] Write Settings + - [ ] Reset Settings +- [ ] **Time** - [ ] Select Time Zone - [ ] Set Alarms -## For Testing +## Quick Start -```python -python -m pip install -r requirements.txt +### Build and Run + +#### Quick Build (Recommended) + +```bash +# One-click build +./build.sh + +# Run application +./build/flock.app/Contents/MacOS/flock # macOS +./build/flock # Linux ``` + +#### Manual Build + +```bash +# Clean build files +./clean.sh + +# Generate Makefile +qmake flock.pro + +# Build +make + +# Run +./build/flock.app/Contents/MacOS/flock # macOS +./build/flock # Linux +``` + +#### Using CMake + +```bash +mkdir build && cd build +cmake .. +make +./flock +``` + +### Usage + +- **Move**: Left-click and drag +- **Menu**: Right-click for context menu +- **Customize**: Change colors, font size, always-on-top behavior + +### Project Structure + +```text +qflock/ +├── build/ # Build output directory +│ ├── flock.app/ # macOS application bundle +│ ├── *.o # Object files +│ ├── moc_*.cpp # Qt meta-object compiler output +│ └── Makefile # Build file +├── build.sh # One-click build script +├── clean.sh # Clean script +├── flock.pro # qmake project file +├── CMakeLists.txt # CMake project file +└── README.md +``` + +### Build Output Management + +- All compilation files (.o, moc_*.cpp, Makefile, etc.) are output to `build/` directory +- Executable files are located at `build/flock.app/Contents/MacOS/flock` (macOS) or `build/flock` (Linux) +- Use `./clean.sh` to quickly clean all build files +- Use `./build.sh` for complete one-click build + +### Next Steps + +- Time zone selection +- Alarm functionality +- Enhanced UI/UX + +## Requirements + +- Qt6 (Core, Widgets) +- C++17 compatible compiler +- CMake 3.16+ (optional, for CMake build) + +## License + +See LICENSE file for details. diff --git a/build.sh b/build.sh new file mode 100755 index 0000000..e08e087 --- /dev/null +++ b/build.sh @@ -0,0 +1,30 @@ +#!/bin/bash +# Build script + +echo "Starting QFlock build..." + +# Create build directory if it doesn't exist +mkdir -p build + +# Clean old build files +echo "Cleaning old build files..." +rm -rf build/* + +# Generate Makefile using qmake +echo "Generating Makefile..." +qmake flock.pro + +# Build project +echo "Starting compilation..." +make + +# Check build result +if [ -f "build/flock.app/Contents/MacOS/flock" ] || [ -f "build/flock" ]; 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)" +else + echo "Build failed!" + exit 1 +fi diff --git a/clean.sh b/clean.sh new file mode 100755 index 0000000..118a202 --- /dev/null +++ b/clean.sh @@ -0,0 +1,18 @@ +#!/bin/bash +# Clean build files script + +echo "Cleaning build files..." + +# Delete files in build directory +if [ -d "build" ]; then + rm -rf build/* + echo "Build directory cleaned" +fi + +# Delete .app bundle +if [ -d "flock.app" ]; then + rm -rf flock.app + echo "flock.app deleted" +fi + +echo "Cleanup complete!" diff --git a/clockwidget.cpp b/clockwidget.cpp new file mode 100644 index 0000000..13175f3 --- /dev/null +++ b/clockwidget.cpp @@ -0,0 +1,214 @@ +#include "clockwidget.h" +#include +#include +#include +#include +#include +#include + +ClockWidget::ClockWidget(QWidget *parent) + : QWidget(parent), m_dragging(false) +{ + setupUI(); + createMenu(); + loadSettings(); + + // Set window properties + setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint | Qt::Tool); + setAttribute(Qt::WA_TranslucentBackground); + setFixedSize(200, 80); + + // Start timer + m_timer = new QTimer(this); + connect(m_timer, &QTimer::timeout, this, &ClockWidget::updateTime); + m_timer->start(1000); + + updateTime(); +} + +ClockWidget::~ClockWidget() +{ + saveSettings(); +} + +void ClockWidget::setupUI() +{ + m_timeLabel = new QLabel(this); + m_timeLabel->setAlignment(Qt::AlignCenter); + + QVBoxLayout *layout = new QVBoxLayout(this); + layout->addWidget(m_timeLabel); + layout->setContentsMargins(10, 10, 10, 10); +} + +void ClockWidget::createMenu() +{ + m_contextMenu = new QMenu(this); + + QAction *alwaysOnTopAction = new QAction("Always on Top", this); + alwaysOnTopAction->setCheckable(true); + alwaysOnTopAction->setChecked(true); + connect(alwaysOnTopAction, &QAction::triggered, this, &ClockWidget::toggleAlwaysOnTop); + + QAction *fontColorAction = new QAction("Set Font Color", this); + connect(fontColorAction, &QAction::triggered, this, &ClockWidget::changeFontColor); + + QAction *bgColorAction = new QAction("Set Background Color", this); + connect(bgColorAction, &QAction::triggered, this, &ClockWidget::changeBackgroundColor); + + QAction *fontSizeAction = new QAction("Set Font Size", this); + connect(fontSizeAction, &QAction::triggered, this, &ClockWidget::changeFontSize); + + QAction *resetAction = new QAction("Reset Settings", this); + connect(resetAction, &QAction::triggered, this, &ClockWidget::resetSettings); + + QAction *quitAction = new QAction("Quit", this); + connect(quitAction, &QAction::triggered, this, &ClockWidget::quitApplication); + + m_contextMenu->addAction(alwaysOnTopAction); + m_contextMenu->addSeparator(); + m_contextMenu->addAction(fontColorAction); + m_contextMenu->addAction(bgColorAction); + m_contextMenu->addAction(fontSizeAction); + m_contextMenu->addSeparator(); + m_contextMenu->addAction(resetAction); + m_contextMenu->addAction(quitAction); +} + +void ClockWidget::mousePressEvent(QMouseEvent *event) +{ + if (event->button() == Qt::LeftButton) + { + m_dragging = true; + m_dragPosition = event->globalPosition().toPoint() - frameGeometry().topLeft(); + event->accept(); + } +} + +void ClockWidget::mouseMoveEvent(QMouseEvent *event) +{ + if (m_dragging && (event->buttons() & Qt::LeftButton)) + { + move(event->globalPosition().toPoint() - m_dragPosition); + event->accept(); + } +} + +void ClockWidget::contextMenuEvent(QContextMenuEvent *event) +{ + m_contextMenu->exec(event->globalPos()); +} + +void ClockWidget::updateTime() +{ + QString currentTime = QTime::currentTime().toString("HH:mm:ss"); + m_timeLabel->setText(currentTime); +} + +void ClockWidget::toggleAlwaysOnTop() +{ + m_alwaysOnTop = !m_alwaysOnTop; + + Qt::WindowFlags flags = windowFlags(); + if (m_alwaysOnTop) + { + flags |= Qt::WindowStaysOnTopHint; + } + else + { + flags &= ~Qt::WindowStaysOnTopHint; + } + setWindowFlags(flags); + show(); +} + +void ClockWidget::changeFontColor() +{ + QColor color = QColorDialog::getColor(m_fontColor, this, "Choose Font Color"); + if (color.isValid()) + { + m_fontColor = color; + updateStyleSheet(); + } +} + +void ClockWidget::changeBackgroundColor() +{ + QColor color = QColorDialog::getColor(m_backgroundColor, this, "Choose Background Color"); + if (color.isValid()) + { + m_backgroundColor = color; + updateStyleSheet(); + } +} + +void ClockWidget::changeFontSize() +{ + bool ok; + int size = QInputDialog::getInt(this, "Set Font Size", "Font Size:", m_fontSize, 8, 72, 1, &ok); + if (ok) + { + m_fontSize = size; + updateStyleSheet(); + } +} + +void ClockWidget::resetSettings() +{ + int ret = QMessageBox::question(this, "Reset Settings", "Are you sure you want to reset all settings?"); + if (ret == QMessageBox::Yes) + { + m_settings->clear(); + loadSettings(); + updateStyleSheet(); + } +} + +void ClockWidget::quitApplication() +{ + qApp->quit(); +} + +void ClockWidget::loadSettings() +{ + m_settings = new QSettings("QFlock", "Clock", this); + + m_fontColor = m_settings->value("fontColor", QColor(255, 255, 255)).value(); + m_backgroundColor = m_settings->value("backgroundColor", QColor(0, 0, 0, 150)).value(); + m_fontSize = m_settings->value("fontSize", 24).toInt(); + m_alwaysOnTop = m_settings->value("alwaysOnTop", true).toBool(); + + // Restore window position + QPoint pos = m_settings->value("position", QPoint(100, 100)).toPoint(); + move(pos); + + updateStyleSheet(); +} + +void ClockWidget::saveSettings() +{ + m_settings->setValue("fontColor", m_fontColor); + m_settings->setValue("backgroundColor", m_backgroundColor); + m_settings->setValue("fontSize", m_fontSize); + m_settings->setValue("alwaysOnTop", m_alwaysOnTop); + m_settings->setValue("position", pos()); +} + +void ClockWidget::updateStyleSheet() +{ + QString styleSheet = QString( + "QLabel { " + "color: %1; " + "font-size: %2pt; " + "font-weight: bold; " + "background-color: %3; " + "border-radius: 10px; " + "padding: 5px; " + "}") + .arg(m_fontColor.name(), + QString::number(m_fontSize), + m_backgroundColor.name(QColor::HexArgb)); + + m_timeLabel->setStyleSheet(styleSheet); + setStyleSheet(QString("background-color: transparent;")); +} diff --git a/clockwidget.h b/clockwidget.h new file mode 100644 index 0000000..22ea4f2 --- /dev/null +++ b/clockwidget.h @@ -0,0 +1,59 @@ +#ifndef CLOCKWIDGET_H +#define CLOCKWIDGET_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +class ClockWidget : public QWidget +{ + Q_OBJECT + +public: + explicit ClockWidget(QWidget *parent = nullptr); + ~ClockWidget(); + +protected: + void mousePressEvent(QMouseEvent *event) override; + void mouseMoveEvent(QMouseEvent *event) override; + void contextMenuEvent(QContextMenuEvent *event) override; + +private slots: + void updateTime(); + void toggleAlwaysOnTop(); + void changeFontColor(); + void changeBackgroundColor(); + void changeFontSize(); + void resetSettings(); + void quitApplication(); + +private: + void setupUI(); + void createMenu(); + void loadSettings(); + void saveSettings(); + void updateStyleSheet(); + + QLabel *m_timeLabel; + QTimer *m_timer; + QMenu *m_contextMenu; + + // Drag related + QPoint m_dragPosition; + bool m_dragging; + + // Settings + QSettings *m_settings; + QColor m_fontColor; + QColor m_backgroundColor; + int m_fontSize; + bool m_alwaysOnTop; +}; + +#endif // CLOCKWIDGET_H diff --git a/config.xml b/config.xml deleted file mode 100644 index ff103a3..0000000 --- a/config.xml +++ /dev/null @@ -1,41 +0,0 @@ - - - - - - 350 - 180 - -1 - -1 - 20 - 2 - #333333 - - - - - 96 - Arial - true - false - #ff6600 - - - - - #1a000000 - - - - - - - - 30 - 4 - 4 - #80000000 - - diff --git a/flock.pro b/flock.pro index f43210e..0e62961 100644 --- a/flock.pro +++ b/flock.pro @@ -1,15 +1,25 @@ # 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 +TARGET = flock QT = core gui widgets SOURCES = \ - $$PWD/main.cpp + $$PWD/main.cpp \ + $$PWD/clockwidget.cpp + +HEADERS = \ + $$PWD/clockwidget.h INCLUDEPATH = \ $$PWD/. -#DEFINES = +# Set build output directories +DESTDIR = $$PWD/build +OBJECTS_DIR = $$PWD/build +MOC_DIR = $$PWD/build +RCC_DIR = $$PWD/build +UI_DIR = $$PWD/build +#DEFINES = diff --git a/main.cpp b/main.cpp index 2bb6039..dd73e3c 100644 --- a/main.cpp +++ b/main.cpp @@ -1,11 +1,15 @@ #include -#include "FloatingClock.h" +#include "clockwidget.h" int main(int argc, char *argv[]) { QApplication app(argc, argv); - FloatingClock clock; + app.setApplicationName("QFlock"); + app.setOrganizationName("QFlock"); + app.setApplicationDisplayName("QFlock"); + + ClockWidget clock; clock.show(); return app.exec();