Files
NumKeyBoard/numkeydia.cpp
T

280 lines
9.7 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#include "numkeydia.h"
#include "ui_numkeydia.h"
#include <QDebug>
#include <QFile>
#include <QKeyEvent>
#include <QMouseEvent>
#include <QPushButton>
#include <QShortcut>
#include <QTextStream>
// --- 私有类定义 ---
class NumKeyDiaPrivate {
Q_DECLARE_PUBLIC(NumKeyDia)
public:
NumKeyDiaPrivate(NumKeyDia *q) : q_ptr(q) {}
// 成员变量移入 D-pointer
Ui::NumKeyDia *ui = nullptr;
QString value;
QPoint m_point;
bool m_firstInputFlag = true;
enum class InputPage { NumberPage, SymbolPage };
InputPage m_inputPage = InputPage::NumberPage;
// 辅助函数
void setInputPage(InputPage page);
void refreshInputButtons();
void configureInputButton(QPushButton *button, const QString &label,
const QString &value, bool enabled, bool isModeSwitch = false);
void loadStyleSheet();
private:
NumKeyDia *q_ptr; // 反向指针
};
// --- 私有类逻辑实现 ---
void NumKeyDiaPrivate::loadStyleSheet() {
QFile file(QStringLiteral(":/commonWidget.qss"));
if (file.open(QFile::ReadOnly | QFile::Text)) {
QTextStream stream(&file);
q_ptr->setStyleSheet(stream.readAll());
}
}
void NumKeyDiaPrivate::setInputPage(InputPage page) {
m_inputPage = page;
refreshInputButtons();
}
void NumKeyDiaPrivate::refreshInputButtons() {
if (m_inputPage == InputPage::NumberPage) {
configureInputButton(ui->btn_1, "1", "1", true);
configureInputButton(ui->btn_2, "2", "2", true);
configureInputButton(ui->btn_3, "3", "3", true);
configureInputButton(ui->btn_4, "4", "4", true);
configureInputButton(ui->btn_5, "5", "5", true);
configureInputButton(ui->btn_6, "6", "6", true);
configureInputButton(ui->btn_7, "7", "7", true);
configureInputButton(ui->btn_8, "8", "8", true);
configureInputButton(ui->btn_9, "9", "9", true);
configureInputButton(ui->btn_sub, "#+=", "", true, true);
configureInputButton(ui->btn_0, "0", "0", true);
configureInputButton(ui->pushButton_dot, ".", ".", true);
} else {
configureInputButton(ui->btn_1, "/", "/", true);
configureInputButton(ui->btn_2, "-", "-", true);
configureInputButton(ui->btn_3, ":", ":", true);
QString deg = QChar(0x00B0);
configureInputButton(ui->btn_4, deg, deg, true);
configureInputButton(ui->btn_5, "'", "'", true);
configureInputButton(ui->btn_6, "\"", "\"", true);
configureInputButton(ui->btn_7, ".", ".", true);
configureInputButton(ui->btn_8, "_", "_", true);
configureInputButton(ui->btn_9, "@", "@", true);
configureInputButton(ui->btn_sub, "123", "", true, true);
configureInputButton(ui->btn_0, "+", "+", true);
configureInputButton(ui->pushButton_dot, ",", ",", true);
}
}
void NumKeyDiaPrivate::configureInputButton(QPushButton *button, const QString &label,
const QString &value, bool enabled, bool isModeSwitch) {
if (!button) return;
button->setText(label);
button->setEnabled(enabled);
button->setProperty("inputValue", value);
button->setProperty("isModeSwitch", isModeSwitch);
}
// --- 公共类实现 ---
NumKeyDia *NumKeyDia::singleton = nullptr;
NumKeyDia::NumKeyDia(QWidget *parent)
: QDialog(parent)
, d_ptr(new NumKeyDiaPrivate(this)) // 初始化 d_ptr
{
Q_D(NumKeyDia);
if (!singleton) singleton = this;
Q_INIT_RESOURCE(NumKeyBoard);
d->ui = new Ui::NumKeyDia;
d->ui->setupUi(this);
d->loadStyleSheet();
setWindowFlags(Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
d->ui->lineEdit_input->setFocusPolicy(Qt::StrongFocus);
auto btnList = d->ui->widget_input->findChildren<QPushButton *>();
for (auto *btn : btnList) {
connect(btn, &QPushButton::clicked, this, &NumKeyDia::btn_input_clicked);
btn->setFocusPolicy(Qt::NoFocus);
// 数字和符号按钮只负责屏幕点按输入,不参与对话框默认确认键竞争。
btn->setAutoDefault(false);
btn->setDefault(false);
}
// 物理键盘 Enter 应默认完成输入;其余操作按钮关闭 autoDefault
// 避免 Qt 把确认键派发到退格/清空等最近获得焦点的按钮。
d->ui->btn_ok->setAutoDefault(true);
d->ui->btn_ok->setDefault(true);
d->ui->btn_back->setAutoDefault(false);
d->ui->btn_clear->setAutoDefault(false);
d->ui->btn_cancel->setAutoDefault(false);
auto *okReturnShortcut = new QShortcut(QKeySequence(Qt::Key_Return), this);
connect(okReturnShortcut, &QShortcut::activated, this, &NumKeyDia::on_btn_ok_clicked);
auto *okEnterShortcut = new QShortcut(QKeySequence(Qt::Key_Enter), this);
connect(okEnterShortcut, &QShortcut::activated, this, &NumKeyDia::on_btn_ok_clicked);
d->ui->toolButton_ico->installEventFilter(this);
d->ui->label_appInfo->installEventFilter(this);
d->ui->btn_clear->setText(QStringLiteral("<"));
d->setInputPage(NumKeyDiaPrivate::InputPage::NumberPage);
}
NumKeyDia::~NumKeyDia() {
if (singleton == this) singleton = nullptr;
// QScopedPointer 会自动删除 d_ptr,但 Ui 需手动销毁(如果没设置 parent)
Q_D(NumKeyDia);
delete d->ui;
}
NumKeyDia* NumKeyDia::instance() {
if(!singleton)
singleton = new NumKeyDia;
return singleton;
}
bool NumKeyDia::eventFilter(QObject *, QEvent *event) {
Q_D(NumKeyDia);
if (event->type() == QEvent::MouseButtonPress) {
auto *mouseEvent = static_cast<QMouseEvent *>(event);
d->m_point = mouseEvent->globalPos() - frameGeometry().topLeft();
} else if (event->type() == QEvent::MouseMove) {
auto *mouseEvent = static_cast<QMouseEvent *>(event);
move(mouseEvent->globalPos() - d->m_point);
}
return false;
}
// 数字键盘物理确认键入口:直接收口到 OK,避免 QLineEdit 或按钮默认行为把 Enter 解释成其它操作。
void NumKeyDia::keyPressEvent(QKeyEvent *event) {
if (event && (event->key() == Qt::Key_Return || event->key() == Qt::Key_Enter)) {
on_btn_ok_clicked();
event->accept();
return;
}
QDialog::keyPressEvent(event);
}
void NumKeyDia::setValue(const QString &value) {
Q_D(NumKeyDia);
d->value = value;
d->setInputPage(NumKeyDiaPrivate::InputPage::NumberPage);
d->ui->lineEdit_input->setFocus();
d->ui->lineEdit_input->setText(value);
d->ui->lineEdit_input->setCursorPosition(d->ui->lineEdit_input->text().size());
d->m_firstInputFlag = true;
}
QString &NumKeyDia::getValue() {
Q_D(NumKeyDia);
return d->value;
}
void NumKeyDia::on_btn_ok_clicked() {
Q_D(NumKeyDia);
QString res = d->ui->lineEdit_input->text();
d->value = res.isEmpty() ? "0" : res;
closeKeyBoard();
}
void NumKeyDia::on_btn_cancel_clicked() {
closeKeyBoard();
}
void NumKeyDia::on_btn_clear_clicked() {
Q_D(NumKeyDia);
d->m_firstInputFlag = false;
int pos = d->ui->lineEdit_input->cursorPosition();
d->ui->lineEdit_input->setCursorPosition(qMax(0, pos - 1));
d->ui->lineEdit_input->setFocus();
}
void NumKeyDia::on_btn_back_clicked() {
Q_D(NumKeyDia);
d->m_firstInputFlag = false;
int index = d->ui->lineEdit_input->cursorPosition();
QString text = d->ui->lineEdit_input->text();
if (index > 0) {
text.remove(index - 1, 1);
d->ui->lineEdit_input->setText(text);
d->ui->lineEdit_input->setCursorPosition(index - 1);
}
d->ui->lineEdit_input->setFocus();
}
// 数字/符号输入按钮统一入口:根据 QLineEdit 当前光标和选区执行插入或替换。
// 设计意图:保持触屏软键盘与普通编辑框一致,首次输入不再隐式清空已有内容;
// 边界条件:无选区时在光标处插入,部分/全部选中时仅替换选区,按钮信号在 GUI 线程内处理。
void NumKeyDia::btn_input_clicked() {
Q_D(NumKeyDia);
auto *btn = qobject_cast<QPushButton *>(sender());
if (!btn) return;
if (btn->property("isModeSwitch").toBool()) {
auto nextPage = (d->m_inputPage == NumKeyDiaPrivate::InputPage::NumberPage)
? NumKeyDiaPrivate::InputPage::SymbolPage
: NumKeyDiaPrivate::InputPage::NumberPage;
d->setInputPage(nextPage);
d->ui->lineEdit_input->setFocus();
return;
}
const QString input = btn->property("inputValue").toString();
if (input.isEmpty()) return;
// 数字键盘按钮点击发生在 GUI 线程内,这里只同步读取当前 QLineEdit 的光标和选区状态,
// 不缓存 QObject 指针到异步流程,避免弹窗复用时选区状态被旧的首次输入标记误清空。
QString text = d->ui->lineEdit_input->text();
const int cursorIndex = d->ui->lineEdit_input->cursorPosition();
const bool hasSelection = d->ui->lineEdit_input->hasSelectedText();
const int selectionStart = d->ui->lineEdit_input->selectionStart();
const int selectedLength = d->ui->lineEdit_input->selectedText().size();
int insertIndex = cursorIndex;
if (hasSelection && selectionStart >= 0) {
// 有选区时仅替换选中范围;全选只是该规则的边界情况,不再依赖首次输入清空整行。
text.replace(selectionStart, selectedLength, input);
insertIndex = selectionStart;
} else {
// 无选区时保持普通编辑语义,在当前光标处插入,支持末尾追加和中间补字符。
text.insert(insertIndex, input);
}
d->m_firstInputFlag = false;
d->ui->lineEdit_input->setText(text);
d->ui->lineEdit_input->setFocus();
d->ui->lineEdit_input->setCursorPosition(insertIndex + input.size());
}
void NumKeyDia::closeKeyBoard() {
this->close();
}
namespace KeyBoard {
NumKeyDia *app() {
return NumKeyDia::instance();
}
}