feat: 主题切换(light/dark QSS)、QSS资源编译修复、托盘图标主题色
- 新增 main_dark.qss 暗色主题样式表 - 使用 .qrc + add_executable 方式确保 QSS 资源正确编译 - Application::applyTheme 动态切换主题和样式表 - 托盘图标 light 主题黑色、dark 主题白色 - Settings 保存后实时应用主题/字体 Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
parent
f8173cd0c1
commit
ae35404d26
@ -139,7 +139,9 @@ else()
|
|||||||
list(APPEND HEADERS src/core/caps_lock_voice_hotkey.h src/core/wayland_text_injector.h)
|
list(APPEND HEADERS src/core/caps_lock_voice_hotkey.h src/core/wayland_text_injector.h)
|
||||||
add_compile_definitions(PLATFORM_LINUX)
|
add_compile_definitions(PLATFORM_LINUX)
|
||||||
endif()
|
endif()
|
||||||
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS})
|
add_executable(${PROJECT_NAME} ${SOURCES} ${HEADERS}
|
||||||
|
src/ui/resources/styles/styles.qrc
|
||||||
|
)
|
||||||
|
|
||||||
target_include_directories(${PROJECT_NAME} PRIVATE
|
target_include_directories(${PROJECT_NAME} PRIVATE
|
||||||
${CMAKE_CURRENT_SOURCE_DIR}/src
|
${CMAKE_CURRENT_SOURCE_DIR}/src
|
||||||
@ -197,15 +199,6 @@ if(WIN32)
|
|||||||
endif()
|
endif()
|
||||||
endif()
|
endif()
|
||||||
|
|
||||||
# ============================================================================
|
|
||||||
# 资源文件
|
|
||||||
# ============================================================================
|
|
||||||
# 样式表
|
|
||||||
qt_add_resources(${PROJECT_NAME} "styles"
|
|
||||||
PREFIX "/"
|
|
||||||
FILES
|
|
||||||
src/ui/resources/styles/main.qss
|
|
||||||
)
|
|
||||||
|
|
||||||
# ============================================================================
|
# ============================================================================
|
||||||
# 安装
|
# 安装
|
||||||
|
|||||||
@ -3,11 +3,21 @@
|
|||||||
#include "core/sense_voice_engine.h"
|
#include "core/sense_voice_engine.h"
|
||||||
#include "utils/logger.h"
|
#include "utils/logger.h"
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
#include <QPalette>
|
||||||
static const char* const kTag = "Application";
|
#include <QColor>
|
||||||
|
#include <QStyleFactory>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QFont>
|
||||||
|
#include <QIcon>
|
||||||
|
#include <QPixmap>
|
||||||
|
#include <QPainter>
|
||||||
|
|
||||||
namespace impress {
|
namespace impress {
|
||||||
|
|
||||||
|
static QString s_currentTheme; // 跟踪当前主题
|
||||||
|
|
||||||
|
static const char* const kTag = "Application";
|
||||||
|
|
||||||
Application::Application(int& argc, char** argv)
|
Application::Application(int& argc, char** argv)
|
||||||
: QApplication(argc, argv)
|
: QApplication(argc, argv)
|
||||||
{
|
{
|
||||||
@ -71,4 +81,88 @@ void Application::loadGlobalModel() {
|
|||||||
sttEngine_->loadModelAsync(modelPath, tokensPath, device, numThreads);
|
sttEngine_->loadModelAsync(modelPath, tokensPath, device, numThreads);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Application::applyTheme(const QString& theme) {
|
||||||
|
s_currentTheme = theme;
|
||||||
|
|
||||||
|
// 1. 先设置风格(必须在 palette 和 stylesheet 之前)
|
||||||
|
qApp->setStyle(QStyleFactory::create("Fusion"));
|
||||||
|
|
||||||
|
// 2. 设置调色板
|
||||||
|
QPalette palette;
|
||||||
|
if (theme == "dark") {
|
||||||
|
palette.setColor(QPalette::Window, QColor(53, 53, 53));
|
||||||
|
palette.setColor(QPalette::WindowText, Qt::white);
|
||||||
|
palette.setColor(QPalette::Base, QColor(25, 25, 25));
|
||||||
|
palette.setColor(QPalette::AlternateBase, QColor(53, 53, 53));
|
||||||
|
palette.setColor(QPalette::ToolTipBase, Qt::white);
|
||||||
|
palette.setColor(QPalette::ToolTipText, Qt::white);
|
||||||
|
palette.setColor(QPalette::Text, Qt::white);
|
||||||
|
palette.setColor(QPalette::Button, QColor(53, 53, 53));
|
||||||
|
palette.setColor(QPalette::ButtonText, Qt::white);
|
||||||
|
palette.setColor(QPalette::BrightText, Qt::cyan);
|
||||||
|
palette.setColor(QPalette::Link, QColor(42, 130, 218));
|
||||||
|
palette.setColor(QPalette::Highlight, QColor(42, 130, 218));
|
||||||
|
palette.setColor(QPalette::HighlightedText, Qt::black);
|
||||||
|
palette.setColor(QPalette::Disabled, QPalette::Text, QColor(127, 127, 127));
|
||||||
|
palette.setColor(QPalette::Disabled, QPalette::ButtonText, QColor(127, 127, 127));
|
||||||
|
palette.setColor(QPalette::Disabled, QPalette::WindowText, QColor(127, 127, 127));
|
||||||
|
palette.setColor(QPalette::Disabled, QPalette::Highlight, QColor(80, 80, 80));
|
||||||
|
palette.setColor(QPalette::Disabled, QPalette::HighlightedText, QColor(127, 127, 127));
|
||||||
|
} else {
|
||||||
|
palette = qApp->style()->standardPalette();
|
||||||
|
}
|
||||||
|
qApp->setPalette(palette);
|
||||||
|
|
||||||
|
// 3. 最后设置样式表(覆盖 palette)
|
||||||
|
const QString qssPath = (theme == "dark") ? ":/styles/main_dark.qss" : ":/styles/main.qss";
|
||||||
|
QFile styleFile(qssPath);
|
||||||
|
if (styleFile.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
||||||
|
qApp->setStyleSheet(styleFile.readAll());
|
||||||
|
styleFile.close();
|
||||||
|
} else {
|
||||||
|
LOG_ERROR("Theme", QString("无法加载样式表: %1").arg(qssPath));
|
||||||
|
}
|
||||||
|
|
||||||
|
LOG_INFO("Theme", QString("主题已切换: %1").arg(theme));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Application::applyFontSize(int size) {
|
||||||
|
QFont font = qApp->font();
|
||||||
|
font.setPointSize(size);
|
||||||
|
qApp->setFont(font);
|
||||||
|
LOG_INFO("Theme", QString("字体大小已设置: %1").arg(size));
|
||||||
|
}
|
||||||
|
|
||||||
|
QIcon Application::createTrayIcon(bool active) {
|
||||||
|
const QColor color = (s_currentTheme == "dark") ? Qt::white : Qt::black;
|
||||||
|
const int size = 16;
|
||||||
|
|
||||||
|
QPixmap pixmap(size, size);
|
||||||
|
pixmap.fill(Qt::transparent);
|
||||||
|
QPainter painter(&pixmap);
|
||||||
|
painter.setRenderHint(QPainter::Antialiasing);
|
||||||
|
painter.setPen(Qt::NoPen);
|
||||||
|
painter.setBrush(color);
|
||||||
|
|
||||||
|
if (active) {
|
||||||
|
// 播放图标(三角形)
|
||||||
|
const int margin = 3;
|
||||||
|
QPolygon triangle;
|
||||||
|
triangle << QPoint(margin, margin)
|
||||||
|
<< QPoint(margin, size - margin)
|
||||||
|
<< QPoint(size - margin, size / 2);
|
||||||
|
painter.drawPolygon(triangle);
|
||||||
|
} else {
|
||||||
|
// 停止图标(正方形)
|
||||||
|
const int margin = 3;
|
||||||
|
painter.drawRect(margin, margin, size - 2 * margin, size - 2 * margin);
|
||||||
|
}
|
||||||
|
|
||||||
|
return QIcon(pixmap);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool Application::isDarkTheme() {
|
||||||
|
return s_currentTheme == "dark";
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace impress
|
} // namespace impress
|
||||||
|
|||||||
@ -35,6 +35,18 @@ public:
|
|||||||
/** @brief 加载全局模型(在配置加载后手动调用) */
|
/** @brief 加载全局模型(在配置加载后手动调用) */
|
||||||
void loadGlobalModel();
|
void loadGlobalModel();
|
||||||
|
|
||||||
|
/** @brief 应用主题(light/dark),可在运行时调用 */
|
||||||
|
static void applyTheme(const QString& theme);
|
||||||
|
|
||||||
|
/** @brief 应用全局字体大小 */
|
||||||
|
static void applyFontSize(int size);
|
||||||
|
|
||||||
|
/** @brief 生成托盘图标(light=黑色,dark=白色) */
|
||||||
|
static QIcon createTrayIcon(bool active);
|
||||||
|
|
||||||
|
/** @brief 当前主题是否为 dark */
|
||||||
|
static bool isDarkTheme();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
/** @brief 模型加载中(带路径) */
|
/** @brief 模型加载中(带路径) */
|
||||||
void modelLoading(const QString& modelPath);
|
void modelLoading(const QString& modelPath);
|
||||||
|
|||||||
@ -7,6 +7,7 @@
|
|||||||
#include <QDir>
|
#include <QDir>
|
||||||
#include <QStandardPaths>
|
#include <QStandardPaths>
|
||||||
#include <QCommandLineParser>
|
#include <QCommandLineParser>
|
||||||
|
#include <QApplication>
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
@ -66,6 +67,13 @@ int main(int argc, char* argv[])
|
|||||||
configManager->set("stt.model_path", modelPath);
|
configManager->set("stt.model_path", modelPath);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 应用主题和字体
|
||||||
|
QString theme = configManager->get("ui.theme").toString();
|
||||||
|
int fontSize = configManager->get("ui.font_size").toInt();
|
||||||
|
impress::Application::applyTheme(theme);
|
||||||
|
if (fontSize > 0) impress::Application::applyFontSize(fontSize);
|
||||||
|
LOG_INFO("Main", QString("主题: %1, 字体: %2").arg(theme).arg(fontSize));
|
||||||
|
|
||||||
// 配置加载完成后,启动全局模型加载
|
// 配置加载完成后,启动全局模型加载
|
||||||
app.loadGlobalModel();
|
app.loadGlobalModel();
|
||||||
|
|
||||||
|
|||||||
@ -5,6 +5,7 @@
|
|||||||
#include "core/voice_input_service.h"
|
#include "core/voice_input_service.h"
|
||||||
#include "core/sense_voice_engine.h"
|
#include "core/sense_voice_engine.h"
|
||||||
#include "app/config_manager.h"
|
#include "app/config_manager.h"
|
||||||
|
#include "app/application.h"
|
||||||
#include "utils/logger.h"
|
#include "utils/logger.h"
|
||||||
|
|
||||||
#include <QMenuBar>
|
#include <QMenuBar>
|
||||||
@ -37,7 +38,6 @@ MainWindow::MainWindow(ConfigManager* configManager,
|
|||||||
setupMenuBar();
|
setupMenuBar();
|
||||||
setupStatusBar(sttEngine);
|
setupStatusBar(sttEngine);
|
||||||
setupTrayIcon();
|
setupTrayIcon();
|
||||||
loadStyleSheet();
|
|
||||||
|
|
||||||
// 初始化语音输入服务(共享全局引擎)
|
// 初始化语音输入服务(共享全局引擎)
|
||||||
voiceInputService_ = new VoiceInputService(configManager_, sttEngine, this);
|
voiceInputService_ = new VoiceInputService(configManager_, sttEngine, this);
|
||||||
@ -126,9 +126,9 @@ void MainWindow::setupTrayIcon() {
|
|||||||
trayIcon_ = new QSystemTrayIcon(this);
|
trayIcon_ = new QSystemTrayIcon(this);
|
||||||
trayIcon_->setContextMenu(trayMenu_);
|
trayIcon_->setContextMenu(trayMenu_);
|
||||||
|
|
||||||
// 默认状态:停止图标(SP_MediaStop)
|
// 默认状态:根据主题颜色生成图标
|
||||||
idleIcon_ = style()->standardIcon(QStyle::SP_MediaStop);
|
idleIcon_ = Application::createTrayIcon(false);
|
||||||
activeIcon_ = style()->standardIcon(QStyle::SP_MediaPlay);
|
activeIcon_ = Application::createTrayIcon(true);
|
||||||
|
|
||||||
trayIcon_->setIcon(idleIcon_);
|
trayIcon_->setIcon(idleIcon_);
|
||||||
trayIcon_->setToolTip("Impress Voice Input - 语音输入就绪");
|
trayIcon_->setToolTip("Impress Voice Input - 语音输入就绪");
|
||||||
@ -236,6 +236,17 @@ void MainWindow::updateModelStatus() {
|
|||||||
void MainWindow::onVoiceInputConfigChanged() {
|
void MainWindow::onVoiceInputConfigChanged() {
|
||||||
if (!voiceInputService_) return;
|
if (!voiceInputService_) return;
|
||||||
|
|
||||||
|
// 动态应用主题和字体
|
||||||
|
QString theme = configManager_->get("ui.theme").toString();
|
||||||
|
int fontSize = configManager_->get("ui.font_size").toInt();
|
||||||
|
Application::applyTheme(theme);
|
||||||
|
if (fontSize > 0) Application::applyFontSize(fontSize);
|
||||||
|
|
||||||
|
// 刷新托盘图标颜色
|
||||||
|
idleIcon_ = Application::createTrayIcon(false);
|
||||||
|
activeIcon_ = Application::createTrayIcon(true);
|
||||||
|
updateTrayIcon("语音输入就绪");
|
||||||
|
|
||||||
// 更新模型状态显示
|
// 更新模型状态显示
|
||||||
updateModelStatus();
|
updateModelStatus();
|
||||||
|
|
||||||
|
|||||||
@ -1,143 +1,157 @@
|
|||||||
/* Impress Voice Input - 全局样式表 */
|
/* Impress Voice Input - 亮色主题样式表 */
|
||||||
|
|
||||||
/* ========== 全局 ========== */
|
/* ========== 全局 ========== */
|
||||||
* {
|
* {
|
||||||
font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
|
font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMainWindow, QWidget {
|
QWidget {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 容器控件必须显式设置背景 */
|
||||||
|
QFrame {
|
||||||
|
background-color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollArea, QScrollArea > QWidget {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
color: #2c3e50;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QTabWidget ========== */
|
/* ========== QTabWidget ========== */
|
||||||
QTabWidget::pane {
|
QTabWidget::pane {
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #e0e0e0;
|
||||||
border-radius: 4px;
|
border-radius: 6px;
|
||||||
background: #fafafa;
|
background: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTabBar::tab {
|
QTabBar::tab {
|
||||||
background: #f0f2f5;
|
background: #f5f5f5;
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #e0e0e0;
|
||||||
border-bottom: none;
|
border-bottom: none;
|
||||||
border-top-left-radius: 4px;
|
border-top-left-radius: 6px;
|
||||||
border-top-right-radius: 4px;
|
border-top-right-radius: 6px;
|
||||||
padding: 10px 24px;
|
padding: 10px 24px;
|
||||||
margin-right: 2px;
|
margin-right: 2px;
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: #606266;
|
color: #666666;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTabBar::tab:selected {
|
QTabBar::tab:selected {
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
border-bottom: 2px solid #409eff;
|
border-bottom: 2px solid #1976d2;
|
||||||
color: #409eff;
|
color: #1976d2;
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTabBar::tab:hover {
|
QTabBar::tab:hover {
|
||||||
color: #409eff;
|
color: #1976d2;
|
||||||
|
background: #e8eaf6;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QPushButton ========== */
|
/* ========== QPushButton ========== */
|
||||||
QPushButton {
|
QPushButton {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #d0d0d0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 6px 16px;
|
padding: 6px 16px;
|
||||||
color: #606266;
|
color: #333333;
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton:hover {
|
QPushButton:hover {
|
||||||
background-color: #ecf5ff;
|
background-color: #e3f2fd;
|
||||||
border-color: #b3d8ff;
|
border-color: #1976d2;
|
||||||
color: #409eff;
|
color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton:pressed {
|
QPushButton:pressed {
|
||||||
background-color: #e6f0ff;
|
background-color: #bbdefb;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 主要操作按钮 */
|
/* 主要操作按钮 */
|
||||||
QPushButton[objectName="saveBtn"],
|
QPushButton[objectName="saveBtn"],
|
||||||
QPushButton[text="保存配置"] {
|
QPushButton[text="保存配置"] {
|
||||||
background-color: #409eff;
|
background-color: #1976d2;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border: 1px solid #409eff;
|
border: 1px solid #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton[objectName="saveBtn"]:hover,
|
QPushButton[objectName="saveBtn"]:hover,
|
||||||
QPushButton[text="保存配置"]:hover {
|
QPushButton[text="保存配置"]:hover {
|
||||||
background-color: #66b1ff;
|
background-color: #1565c0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* 危险操作按钮 */
|
/* 危险操作按钮 */
|
||||||
QPushButton[text="停止"],
|
QPushButton[text="停止"],
|
||||||
QPushButton[text="停止录音"] {
|
QPushButton[text="停止录音"] {
|
||||||
background-color: #f56c6c;
|
background-color: #e53935;
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
border: 1px solid #f56c6c;
|
border: 1px solid #e53935;
|
||||||
}
|
}
|
||||||
|
|
||||||
QPushButton[text="停止"]:hover,
|
QPushButton[text="停止"]:hover,
|
||||||
QPushButton[text="停止录音"]:hover {
|
QPushButton[text="停止录音"]:hover {
|
||||||
background-color: #f78989;
|
background-color: #c62828;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QGroupBox ========== */
|
/* ========== QGroupBox ========== */
|
||||||
QGroupBox {
|
QGroupBox {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #e0e0e0;
|
||||||
border-radius: 6px;
|
border-radius: 6px;
|
||||||
margin-top: 12px;
|
margin-top: 12px;
|
||||||
padding-top: 16px;
|
padding-top: 16px;
|
||||||
|
color: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
|
||||||
QGroupBox::title {
|
QGroupBox::title {
|
||||||
subcontrol-origin: margin;
|
subcontrol-origin: margin;
|
||||||
subcontrol-position: top left;
|
subcontrol-position: top left;
|
||||||
padding: 0 8px;
|
padding: 0 8px;
|
||||||
color: #303133;
|
color: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QLabel ========== */
|
/* ========== QLabel ========== */
|
||||||
QLabel {
|
QLabel {
|
||||||
color: #606266;
|
color: #333333;
|
||||||
|
background: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QLineEdit ========== */
|
/* ========== QLineEdit ========== */
|
||||||
QLineEdit {
|
QLineEdit {
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #d0d0d0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
|
color: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLineEdit:focus {
|
QLineEdit:focus {
|
||||||
border-color: #409eff;
|
border-color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
QLineEdit[readOnly="true"] {
|
QLineEdit[readOnly="true"] {
|
||||||
background-color: #f5f7fa;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QComboBox ========== */
|
/* ========== QComboBox ========== */
|
||||||
QComboBox {
|
QComboBox {
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #d0d0d0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 5px 10px;
|
padding: 5px 10px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
|
color: #1a1a1a;
|
||||||
min-width: 120px;
|
min-width: 120px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QComboBox:hover {
|
QComboBox:hover {
|
||||||
border-color: #c0c4cc;
|
border-color: #909090;
|
||||||
}
|
}
|
||||||
|
|
||||||
QComboBox:focus {
|
QComboBox:focus {
|
||||||
border-color: #409eff;
|
border-color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
QComboBox::drop-down {
|
QComboBox::drop-down {
|
||||||
@ -145,52 +159,62 @@ QComboBox::drop-down {
|
|||||||
width: 24px;
|
width: 24px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QComboBox QAbstractItemView {
|
||||||
|
background-color: #ffffff;
|
||||||
|
color: #1a1a1a;
|
||||||
|
selection-background-color: #e3f2fd;
|
||||||
|
}
|
||||||
|
|
||||||
/* ========== QSpinBox / QDoubleSpinBox ========== */
|
/* ========== QSpinBox / QDoubleSpinBox ========== */
|
||||||
QSpinBox, QDoubleSpinBox {
|
QSpinBox, QDoubleSpinBox {
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #d0d0d0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 4px 8px;
|
padding: 4px 8px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
|
color: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
|
||||||
QSpinBox:focus, QDoubleSpinBox:focus {
|
QSpinBox:focus, QDoubleSpinBox:focus {
|
||||||
border-color: #409eff;
|
border-color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QProgressBar ========== */
|
/* ========== QProgressBar ========== */
|
||||||
QProgressBar {
|
QProgressBar {
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #e0e0e0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
text-align: center;
|
text-align: center;
|
||||||
height: 20px;
|
height: 20px;
|
||||||
background: #f5f7fa;
|
background: #f5f5f5;
|
||||||
|
color: #333333;
|
||||||
}
|
}
|
||||||
|
|
||||||
QProgressBar::chunk {
|
QProgressBar::chunk {
|
||||||
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
||||||
stop:0 #409eff, stop:1 #66b1ff);
|
stop:0 #1976d2, stop:1 #42a5f5);
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QTextEdit ========== */
|
/* ========== QTextEdit ========== */
|
||||||
QTextEdit {
|
QTextEdit {
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #d0d0d0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 8px;
|
padding: 8px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
selection-background-color: #ecf5ff;
|
color: #1a1a1a;
|
||||||
|
selection-background-color: #e3f2fd;
|
||||||
}
|
}
|
||||||
|
|
||||||
QTextEdit:focus {
|
QTextEdit:focus {
|
||||||
border-color: #409eff;
|
border-color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QListWidget ========== */
|
/* ========== QListWidget ========== */
|
||||||
QListWidget {
|
QListWidget {
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #e0e0e0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
background: #ffffff;
|
background: #ffffff;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
color: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
|
||||||
QListWidget::item {
|
QListWidget::item {
|
||||||
@ -199,58 +223,62 @@ QListWidget::item {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QListWidget::item:selected {
|
QListWidget::item:selected {
|
||||||
background-color: #ecf5ff;
|
background-color: #e3f2fd;
|
||||||
color: #409eff;
|
color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
QListWidget::item:hover {
|
QListWidget::item:hover {
|
||||||
background-color: #f5f7fa;
|
background-color: #f5f5f5;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QCheckBox ========== */
|
/* ========== QCheckBox ========== */
|
||||||
QCheckBox {
|
QCheckBox {
|
||||||
spacing: 8px;
|
spacing: 8px;
|
||||||
|
color: #333333;
|
||||||
}
|
}
|
||||||
|
|
||||||
QCheckBox::indicator {
|
QCheckBox::indicator {
|
||||||
width: 16px;
|
width: 16px;
|
||||||
height: 16px;
|
height: 16px;
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #d0d0d0;
|
||||||
border-radius: 3px;
|
border-radius: 3px;
|
||||||
|
background-color: #ffffff;
|
||||||
}
|
}
|
||||||
|
|
||||||
QCheckBox::indicator:checked {
|
QCheckBox::indicator:checked {
|
||||||
background-color: #409eff;
|
background-color: #1976d2;
|
||||||
border-color: #409eff;
|
border-color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== QMenu / QMenuBar ========== */
|
/* ========== QMenu / QMenuBar ========== */
|
||||||
QMenuBar {
|
QMenuBar {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border-bottom: 1px solid #dcdfe6;
|
border-bottom: 1px solid #e0e0e0;
|
||||||
padding: 2px;
|
padding: 2px;
|
||||||
|
color: #333333;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMenuBar::item:selected {
|
QMenuBar::item:selected {
|
||||||
background-color: #ecf5ff;
|
background-color: #e3f2fd;
|
||||||
color: #409eff;
|
color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMenu {
|
QMenu {
|
||||||
background-color: #ffffff;
|
background-color: #ffffff;
|
||||||
border: 1px solid #dcdfe6;
|
border: 1px solid #e0e0e0;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
padding: 4px;
|
padding: 4px;
|
||||||
|
color: #333333;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMenu::item:selected {
|
QMenu::item:selected {
|
||||||
background-color: #ecf5ff;
|
background-color: #e3f2fd;
|
||||||
color: #409eff;
|
color: #1976d2;
|
||||||
}
|
}
|
||||||
|
|
||||||
QMenu::separator {
|
QMenu::separator {
|
||||||
height: 1px;
|
height: 1px;
|
||||||
background-color: #ebeef5;
|
background-color: #e0e0e0;
|
||||||
margin: 4px 0;
|
margin: 4px 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -260,25 +288,25 @@ QMessageBox {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QMessageBox QLabel {
|
QMessageBox QLabel {
|
||||||
color: #303133;
|
color: #1a1a1a;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ========== 滚动条 ========== */
|
/* ========== 滚动条 ========== */
|
||||||
QScrollBar:vertical {
|
QScrollBar:vertical {
|
||||||
border: none;
|
border: none;
|
||||||
background: #f5f7fa;
|
background: #f5f5f5;
|
||||||
width: 8px;
|
width: 8px;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QScrollBar::handle:vertical {
|
QScrollBar::handle:vertical {
|
||||||
background: #c0c4cc;
|
background: #bdbdbd;
|
||||||
border-radius: 4px;
|
border-radius: 4px;
|
||||||
min-height: 30px;
|
min-height: 30px;
|
||||||
}
|
}
|
||||||
|
|
||||||
QScrollBar::handle:vertical:hover {
|
QScrollBar::handle:vertical:hover {
|
||||||
background: #909399;
|
background: #9e9e9e;
|
||||||
}
|
}
|
||||||
|
|
||||||
QScrollBar::add-line:vertical,
|
QScrollBar::add-line:vertical,
|
||||||
|
|||||||
312
src/ui/resources/styles/main_dark.qss
Normal file
312
src/ui/resources/styles/main_dark.qss
Normal file
@ -0,0 +1,312 @@
|
|||||||
|
/* Impress Voice Input - 暗色主题样式表 */
|
||||||
|
|
||||||
|
/* ========== 全局 ========== */
|
||||||
|
* {
|
||||||
|
font-family: "PingFang SC", "Microsoft YaHei", "Noto Sans CJK SC", sans-serif;
|
||||||
|
}
|
||||||
|
|
||||||
|
QWidget {
|
||||||
|
background-color: #353535;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QFrame {
|
||||||
|
background-color: #353535;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollArea, QScrollArea > QWidget {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QTabWidget ========== */
|
||||||
|
QTabWidget::pane {
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTabBar::tab {
|
||||||
|
background: #3a3a3a;
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-bottom: none;
|
||||||
|
border-top-left-radius: 4px;
|
||||||
|
border-top-right-radius: 4px;
|
||||||
|
padding: 10px 24px;
|
||||||
|
margin-right: 2px;
|
||||||
|
font-size: 14px;
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTabBar::tab:selected {
|
||||||
|
background: #2a2a2a;
|
||||||
|
border-bottom: 2px solid #4286f4;
|
||||||
|
color: #4286f4;
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTabBar::tab:hover {
|
||||||
|
color: #4286f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QPushButton ========== */
|
||||||
|
QPushButton {
|
||||||
|
background-color: #353535;
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 6px 16px;
|
||||||
|
color: #cccccc;
|
||||||
|
font-size: 13px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton:hover {
|
||||||
|
background-color: #4286f4;
|
||||||
|
border-color: #4286f4;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton:pressed {
|
||||||
|
background-color: #3a6fd8;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 主要操作按钮 */
|
||||||
|
QPushButton[objectName="saveBtn"],
|
||||||
|
QPushButton[text="保存配置"] {
|
||||||
|
background-color: #4286f4;
|
||||||
|
color: #ffffff;
|
||||||
|
border: 1px solid #4286f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton[objectName="saveBtn"]:hover,
|
||||||
|
QPushButton[text="保存配置"]:hover {
|
||||||
|
background-color: #5a9aff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 危险操作按钮 */
|
||||||
|
QPushButton[text="停止"],
|
||||||
|
QPushButton[text="停止录音"] {
|
||||||
|
background-color: #e74c3c;
|
||||||
|
color: #ffffff;
|
||||||
|
border: 1px solid #e74c3c;
|
||||||
|
}
|
||||||
|
|
||||||
|
QPushButton[text="停止"]:hover,
|
||||||
|
QPushButton[text="停止录音"]:hover {
|
||||||
|
background-color: #f05e50;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QGroupBox ========== */
|
||||||
|
QGroupBox {
|
||||||
|
font-weight: bold;
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 6px;
|
||||||
|
margin-top: 12px;
|
||||||
|
padding-top: 16px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QGroupBox::title {
|
||||||
|
subcontrol-origin: margin;
|
||||||
|
subcontrol-position: top left;
|
||||||
|
padding: 0 8px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QLabel ========== */
|
||||||
|
QLabel {
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QLineEdit ========== */
|
||||||
|
QLineEdit {
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLineEdit:focus {
|
||||||
|
border-color: #4286f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
QLineEdit[readOnly="true"] {
|
||||||
|
background-color: #2a2a2a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QComboBox ========== */
|
||||||
|
QComboBox {
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 5px 10px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
color: #ffffff;
|
||||||
|
min-width: 120px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QComboBox:hover {
|
||||||
|
border-color: #888888;
|
||||||
|
}
|
||||||
|
|
||||||
|
QComboBox:focus {
|
||||||
|
border-color: #4286f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
QComboBox::drop-down {
|
||||||
|
border: none;
|
||||||
|
width: 24px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QComboBox QAbstractItemView {
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
color: #ffffff;
|
||||||
|
selection-background-color: #4286f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QSpinBox / QDoubleSpinBox ========== */
|
||||||
|
QSpinBox, QDoubleSpinBox {
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px 8px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QSpinBox:focus, QDoubleSpinBox:focus {
|
||||||
|
border-color: #4286f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QProgressBar ========== */
|
||||||
|
QProgressBar {
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 4px;
|
||||||
|
text-align: center;
|
||||||
|
height: 20px;
|
||||||
|
background: #2a2a2a;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QProgressBar::chunk {
|
||||||
|
background: qlineargradient(x1:0, y1:0, x2:1, y2:0,
|
||||||
|
stop:0 #4286f4, stop:1 #5a9aff);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QTextEdit ========== */
|
||||||
|
QTextEdit {
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 8px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
color: #ffffff;
|
||||||
|
selection-background-color: #4286f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
QTextEdit:focus {
|
||||||
|
border-color: #4286f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QListWidget ========== */
|
||||||
|
QListWidget {
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 4px;
|
||||||
|
background: #1a1a1a;
|
||||||
|
padding: 4px;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QListWidget::item {
|
||||||
|
padding: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QListWidget::item:selected {
|
||||||
|
background-color: #4286f4;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QListWidget::item:hover {
|
||||||
|
background-color: #3a3a3a;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QCheckBox ========== */
|
||||||
|
QCheckBox {
|
||||||
|
spacing: 8px;
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCheckBox::indicator {
|
||||||
|
width: 16px;
|
||||||
|
height: 16px;
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 3px;
|
||||||
|
background-color: #1a1a1a;
|
||||||
|
}
|
||||||
|
|
||||||
|
QCheckBox::indicator:checked {
|
||||||
|
background-color: #4286f4;
|
||||||
|
border-color: #4286f4;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QMenu / QMenuBar ========== */
|
||||||
|
QMenuBar {
|
||||||
|
background-color: #353535;
|
||||||
|
border-bottom: 1px solid #555555;
|
||||||
|
padding: 2px;
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenuBar::item:selected {
|
||||||
|
background-color: #4286f4;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu {
|
||||||
|
background-color: #353535;
|
||||||
|
border: 1px solid #555555;
|
||||||
|
border-radius: 4px;
|
||||||
|
padding: 4px;
|
||||||
|
color: #cccccc;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu::item:selected {
|
||||||
|
background-color: #4286f4;
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMenu::separator {
|
||||||
|
height: 1px;
|
||||||
|
background-color: #555555;
|
||||||
|
margin: 4px 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== QMessageBox ========== */
|
||||||
|
QMessageBox {
|
||||||
|
background-color: #353535;
|
||||||
|
}
|
||||||
|
|
||||||
|
QMessageBox QLabel {
|
||||||
|
color: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ========== 滚动条 ========== */
|
||||||
|
QScrollBar:vertical {
|
||||||
|
border: none;
|
||||||
|
background: #2a2a2a;
|
||||||
|
width: 8px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::handle:vertical {
|
||||||
|
background: #666666;
|
||||||
|
border-radius: 4px;
|
||||||
|
min-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::handle:vertical:hover {
|
||||||
|
background: #888888;
|
||||||
|
}
|
||||||
|
|
||||||
|
QScrollBar::add-line:vertical,
|
||||||
|
QScrollBar::sub-line:vertical {
|
||||||
|
height: 0;
|
||||||
|
}
|
||||||
6
src/ui/resources/styles/styles.qrc
Normal file
6
src/ui/resources/styles/styles.qrc
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
<RCC>
|
||||||
|
<qresource prefix="/styles">
|
||||||
|
<file>main.qss</file>
|
||||||
|
<file>main_dark.qss</file>
|
||||||
|
</qresource>
|
||||||
|
</RCC>
|
||||||
Loading…
Reference in New Issue
Block a user