From 49b39b74d22cef19039f25a55c05f8972b72cda2 Mon Sep 17 00:00:00 2001 From: ivaquero Date: Sat, 18 Apr 2026 01:53:51 +0800 Subject: [PATCH] add config.xml --- CMakeLists.txt | 2 + README.md | 26 ++++++-- XML_CONFIG_GUIDE.md | 78 +++++++++++++++++++++++ clockwidget.cpp | 58 ++++++++++++++---- clockwidget.h | 6 +- config.xml | 8 +++ configmanager.cpp | 146 ++++++++++++++++++++++++++++++++++++++++++++ configmanager.h | 56 +++++++++++++++++ flock.pro | 6 +- test_xml_config.sh | 29 +++++++++ 10 files changed, 391 insertions(+), 24 deletions(-) create mode 100644 XML_CONFIG_GUIDE.md create mode 100644 config.xml create mode 100644 configmanager.cpp create mode 100644 configmanager.h create mode 100755 test_xml_config.sh diff --git a/CMakeLists.txt b/CMakeLists.txt index 0a64cdc..bc69017 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -14,10 +14,12 @@ qt6_standard_project_setup() set(SOURCES main.cpp clockwidget.cpp + configmanager.cpp ) set(HEADERS clockwidget.h + configmanager.h ) # Create executable diff --git a/README.md b/README.md index 41ab8ed..d558c56 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# ⏰ Flock +# ⏰ 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) @@ -18,16 +18,16 @@ When on macOS / Linux, the clock on the menu bar is too small to be noticeable, - [ ] Frameless - [x] Drag and Move - [x] Right Click Menu - - [ ] Always On Top + - [x] Always On Top - [ ] Show in Fullscreen Mode - [x] **Style** - [x] Set Background Color - [x] Set Font Color - [x] Set FontSize -- [ ] **Configuration File** (XML) - - [ ] Read Settings - - [ ] Write Settings - - [ ] Reset Settings +- [x] **Configuration File** (XML) + - [x] Read Settings + - [x] Write Settings + - [x] Reset Settings - [ ] **Time** - [ ] Select Time Zone - [ ] Set Alarms @@ -73,11 +73,24 @@ make ./flock ``` +### Configuration + +The application now uses XML configuration files instead of QSettings. Configuration is stored in `config.xml` with the following options: + +- **FontColor**: Font color in hex format +- **BackgroundColor**: Background color with alpha transparency +- **FontSize**: Font size in points (8-72) +- **AlwaysOnTop**: Window always stays on top +- **WindowPosition**: Window position coordinates + +The configuration file is automatically created when you first run the application and make changes to settings. + ### Usage - **Move**: Left-click and drag - **Menu**: Right-click for context menu - **Customize**: Change colors, font size, always-on-top behavior +- **Configuration**: Settings are saved to `config.xml` and persist between sessions ### Project Structure @@ -88,6 +101,7 @@ qflock/ │ ├── *.o # Object files │ ├── moc_*.cpp # Qt meta-object compiler output │ └── Makefile # Build file +├── config.xml # XML configuration file ├── build.sh # One-click build script ├── clean.sh # Clean script ├── flock.pro # qmake project file diff --git a/XML_CONFIG_GUIDE.md b/XML_CONFIG_GUIDE.md new file mode 100644 index 0000000..e85f1e6 --- /dev/null +++ b/XML_CONFIG_GUIDE.md @@ -0,0 +1,78 @@ +# 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 + + + #ffffff + #96000000 + 24 + true + 100,100 + +``` + +## 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. diff --git a/clockwidget.cpp b/clockwidget.cpp index 13175f3..75abef5 100644 --- a/clockwidget.cpp +++ b/clockwidget.cpp @@ -1,4 +1,5 @@ #include "clockwidget.h" +#include "configmanager.h" #include #include #include @@ -128,6 +129,7 @@ void ClockWidget::changeFontColor() if (color.isValid()) { m_fontColor = color; + m_configManager->setFontColor(m_fontColor); updateStyleSheet(); } } @@ -138,6 +140,7 @@ void ClockWidget::changeBackgroundColor() if (color.isValid()) { m_backgroundColor = color; + m_configManager->setBackgroundColor(m_backgroundColor); updateStyleSheet(); } } @@ -149,6 +152,7 @@ void ClockWidget::changeFontSize() if (ok) { m_fontSize = size; + m_configManager->setFontSize(m_fontSize); updateStyleSheet(); } } @@ -158,7 +162,7 @@ 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(); + m_configManager->resetToDefaults(); loadSettings(); updateStyleSheet(); } @@ -171,27 +175,55 @@ void ClockWidget::quitApplication() void ClockWidget::loadSettings() { - m_settings = new QSettings("QFlock", "Clock", this); + m_configManager = new ConfigManager(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(); + // Load from XML config file + if (!m_configManager->loadSettings()) + { + // If loading fails, config manager already has default values + qDebug() << "Using default settings"; + } + + // Apply loaded settings + m_fontColor = m_configManager->fontColor(); + m_backgroundColor = m_configManager->backgroundColor(); + m_fontSize = m_configManager->fontSize(); + m_alwaysOnTop = m_configManager->alwaysOnTop(); // Restore window position - QPoint pos = m_settings->value("position", QPoint(100, 100)).toPoint(); - move(pos); + move(m_configManager->windowPosition()); + + // Update always on top checkbox in menu + QList actions = m_contextMenu->actions(); + for (QAction *action : actions) + { + if (action->text() == "Always on Top") + { + action->setChecked(m_alwaysOnTop); + break; + } + } 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()); + if (!m_configManager) + return; + + // Update config manager with current values + m_configManager->setFontColor(m_fontColor); + m_configManager->setBackgroundColor(m_backgroundColor); + m_configManager->setFontSize(m_fontSize); + m_configManager->setAlwaysOnTop(m_alwaysOnTop); + m_configManager->setWindowPosition(pos()); + + // Save to XML file + if (!m_configManager->saveSettings()) + { + qDebug() << "Failed to save settings to config.xml"; + } } void ClockWidget::updateStyleSheet() diff --git a/clockwidget.h b/clockwidget.h index 22ea4f2..961b133 100644 --- a/clockwidget.h +++ b/clockwidget.h @@ -7,10 +7,10 @@ #include #include #include -#include -#include #include +class ConfigManager; + class ClockWidget : public QWidget { Q_OBJECT @@ -49,7 +49,7 @@ private: bool m_dragging; // Settings - QSettings *m_settings; + ConfigManager *m_configManager; QColor m_fontColor; QColor m_backgroundColor; int m_fontSize; diff --git a/config.xml b/config.xml new file mode 100644 index 0000000..95e0d4b --- /dev/null +++ b/config.xml @@ -0,0 +1,8 @@ + + + #ffffff + #96000000 + 24 + true + 100,100 + diff --git a/configmanager.cpp b/configmanager.cpp new file mode 100644 index 0000000..73f78ad --- /dev/null +++ b/configmanager.cpp @@ -0,0 +1,146 @@ +#include "configmanager.h" +#include +#include + +ConfigManager::ConfigManager(QObject *parent) + : QObject(parent) +{ + setDefaultValues(); +} + +bool ConfigManager::loadSettings(const QString &filename) +{ + QFile file(filename); + if (!file.exists()) + { + qDebug() << "Config file does not exist, using defaults"; + return false; + } + + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + qDebug() << "Cannot open config file for reading:" << file.errorString(); + return false; + } + + QXmlStreamReader reader(&file); + + // Check if it's a valid XML file + if (!reader.readNextStartElement() || reader.name() != "QFlockConfig") + { + qDebug() << "Invalid config file format"; + file.close(); + return false; + } + + // Read configuration values + while (reader.readNextStartElement()) + { + QString name = reader.name().toString(); + QString text = reader.readElementText(); + + if (name == "FontColor") + { + m_fontColor = QColor(text); + } + else if (name == "BackgroundColor") + { + m_backgroundColor = QColor(text); + } + else if (name == "FontSize") + { + m_fontSize = text.toInt(); + } + else if (name == "AlwaysOnTop") + { + m_alwaysOnTop = (text.toLower() == "true"); + } + else if (name == "WindowPosition") + { + // Parse position format: "x,y" + QStringList coords = text.split(','); + if (coords.size() == 2) + { + int x = coords[0].toInt(); + int y = coords[1].toInt(); + m_windowPosition = QPoint(x, y); + } + } + else + { + reader.skipCurrentElement(); + } + } + + file.close(); + + if (reader.hasError()) + { + qDebug() << "Error reading config file:" << reader.errorString(); + return false; + } + + return true; +} + +bool ConfigManager::saveSettings(const QString &filename) +{ + QFile file(filename); + if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + qDebug() << "Cannot open config file for writing:" << file.errorString(); + return false; + } + + QXmlStreamWriter writer(&file); + writer.setAutoFormatting(true); + writer.writeStartDocument(); + + // Root element + writer.writeStartElement("QFlockConfig"); + + // Font color + writer.writeTextElement("FontColor", m_fontColor.name(QColor::HexArgb)); + + // Background color + writer.writeTextElement("BackgroundColor", m_backgroundColor.name(QColor::HexArgb)); + + // Font size + writer.writeTextElement("FontSize", QString::number(m_fontSize)); + + // Always on top + writer.writeTextElement("AlwaysOnTop", m_alwaysOnTop ? "true" : "false"); + + // Window position + QString positionStr = QString("%1,%2") + .arg(m_windowPosition.x()) + .arg(m_windowPosition.y()); + writer.writeTextElement("WindowPosition", positionStr); + + // Close root element + writer.writeEndElement(); + + writer.writeEndDocument(); + file.close(); + + return true; +} + +void ConfigManager::resetToDefaults() +{ + setDefaultValues(); +} + +bool ConfigManager::configExists(const QString &filename) const +{ + return QFile::exists(filename); +} + +void ConfigManager::setDefaultValues() +{ + m_fontColor = QColor(255, 255, 255); // White + m_backgroundColor = QColor(0, 0, 0, 150); // Semi-transparent black + m_fontSize = 24; + m_alwaysOnTop = true; + m_windowPosition = QPoint(100, 100); +} diff --git a/configmanager.h b/configmanager.h new file mode 100644 index 0000000..8fa4b39 --- /dev/null +++ b/configmanager.h @@ -0,0 +1,56 @@ +#ifndef CONFIGMANAGER_H +#define CONFIGMANAGER_H + +#include +#include +#include +#include +#include +#include + +class ConfigManager : public QObject +{ + Q_OBJECT + +public: + explicit ConfigManager(QObject *parent = nullptr); + + // Load settings from XML file + bool loadSettings(const QString &filename = "config.xml"); + + // Save settings to XML file + bool saveSettings(const QString &filename = "config.xml"); + + // Getters + QColor fontColor() const { return m_fontColor; } + QColor backgroundColor() const { return m_backgroundColor; } + int fontSize() const { return m_fontSize; } + bool alwaysOnTop() const { return m_alwaysOnTop; } + QPoint windowPosition() const { return m_windowPosition; } + + // Setters + void setFontColor(const QColor &color) { m_fontColor = color; } + void setBackgroundColor(const QColor &color) { m_backgroundColor = color; } + void setFontSize(int size) { m_fontSize = size; } + void setAlwaysOnTop(bool onTop) { m_alwaysOnTop = onTop; } + void setWindowPosition(const QPoint &pos) { m_windowPosition = pos; } + + // Reset to default values + void resetToDefaults(); + + // Check if config file exists + bool configExists(const QString &filename = "config.xml") const; + +private: + // Default values + void setDefaultValues(); + + // Settings data + QColor m_fontColor; + QColor m_backgroundColor; + int m_fontSize; + bool m_alwaysOnTop; + QPoint m_windowPosition; +}; + +#endif // CONFIGMANAGER_H diff --git a/flock.pro b/flock.pro index 0e62961..7547abd 100644 --- a/flock.pro +++ b/flock.pro @@ -7,10 +7,12 @@ QT = core gui widgets SOURCES = \ $$PWD/main.cpp \ - $$PWD/clockwidget.cpp + $$PWD/clockwidget.cpp \ + $$PWD/configmanager.cpp HEADERS = \ - $$PWD/clockwidget.h + $$PWD/clockwidget.h \ + $$PWD/configmanager.h INCLUDEPATH = \ $$PWD/. diff --git a/test_xml_config.sh b/test_xml_config.sh new file mode 100755 index 0000000..f0dbc85 --- /dev/null +++ b/test_xml_config.sh @@ -0,0 +1,29 @@ +# Build and Run Test + +echo "Building QFlock with XML configuration..." + +# Clean previous build +./clean.sh + +# Build the project +./build.sh + +# Check if build was successful +if [ $? -eq 0 ]; then + echo "Build successful! Testing XML configuration..." + + # Show config file content + echo "Config file content:" + if [ -f "config.xml" ]; then + cat config.xml + else + echo "No config.xml found (will be created on first run)" + fi + + echo "" + echo "You can now run the application:" + echo "./build/flock.app/Contents/MacOS/flock # macOS" + echo "./build/flock # Linux" +else + echo "Build failed!" +fi