新增悬浮按钮管理器及悬浮按钮视图
1. 新增悬浮按钮管理器。其主要作用是负责管理悬浮按钮的可用性及其可见性 2. 新增悬浮按钮视图。其主要作用是负责提供悬浮按钮UI并响应用户输入
This commit is contained in:
parent
16285ebbce
commit
126bb24d7f
|
@ -0,0 +1,10 @@
|
|||
<RCC>
|
||||
<qresource prefix="/floatbutton">
|
||||
<file>img/enablefloatbutton.svg</file>
|
||||
<file>img/disablefloatbutton.svg</file>
|
||||
<file>img/floatbuttondefault.svg</file>
|
||||
<file>img/floatbuttonpressed.svg</file>
|
||||
<file>img/floatbuttonhovered.svg</file>
|
||||
</qresource>
|
||||
<qresource prefix="/"/>
|
||||
</RCC>
|
|
@ -0,0 +1,4 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M2 2L14 14" stroke="#262626" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<path d="M3.76756 3.74718C4.85253 2.66738 6.34833 2 8 2C11.3137 2 14 4.68629 14 8C14 9.64573 13.3374 11.1367 12.2645 12.2207M2.34141 6C2.12031 6.62556 2 7.29873 2 8C2 11.3137 4.68629 14 8 14C8.70127 14 9.37444 13.8797 10 13.6586" stroke="#262626" stroke-width="1.5" stroke-linecap="round"/>
|
||||
</svg>
|
After Width: | Height: | Size: 476 B |
|
@ -0,0 +1,9 @@
|
|||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M6 11H10" stroke="#262626" stroke-width="1.5" stroke-linecap="round"/>
|
||||
<circle cx="6.5" cy="5" r="1" fill="#262626"/>
|
||||
<circle cx="5" cy="8" r="1" fill="#262626"/>
|
||||
<circle cx="11" cy="8" r="1" fill="#262626"/>
|
||||
<circle cx="9.5" cy="5" r="1" fill="#262626"/>
|
||||
<circle cx="8" cy="8" r="6" stroke="#262626" stroke-width="1.5"/>
|
||||
<circle cx="8" cy="8" r="1" fill="#262626"/>
|
||||
</svg>
|
After Width: | Height: | Size: 479 B |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.4 KiB |
|
@ -0,0 +1,2 @@
|
|||
<!DOCTYPE RCC>
|
||||
<RCC version="1.0"/>
|
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.5 KiB |
File diff suppressed because one or more lines are too long
After Width: | Height: | Size: 5.4 KiB |
|
@ -23,6 +23,8 @@ SOURCES += \
|
|||
src/virtualkeyboard/virtualkeyboardmanager.cpp \
|
||||
src/virtualkeyboard/virtualkeyboardmodel.cpp \
|
||||
src/virtualkeyboard/virtualkeyboardview.cpp \
|
||||
src/virtualkeyboardentry/floatbutton.cpp \
|
||||
src/virtualkeyboardentry/floatbuttonmanager.cpp \
|
||||
src/virtualkeyboardentry/virtualkeyboardtrayicon.cpp
|
||||
|
||||
TRANSLATIONS = translations/translation.ts \
|
||||
|
@ -31,6 +33,7 @@ TRANSLATIONS = translations/translation.ts \
|
|||
translations/translation_zh_CN.ts
|
||||
|
||||
RESOURCES += \
|
||||
floatbutton.qrc \
|
||||
qml.qrc \
|
||||
translations.qrc
|
||||
|
||||
|
@ -64,6 +67,9 @@ HEADERS += \
|
|||
src/virtualkeyboard/virtualkeyboardstrategy.h \
|
||||
src/virtualkeyboard/virtualkeyboardview.h \
|
||||
src/virtualkeyboardentry/fcitxvirtualkeyboardservice.h \
|
||||
src/virtualkeyboardentry/floatbutton.h \
|
||||
src/virtualkeyboardentry/floatbuttonmanager.h \
|
||||
src/virtualkeyboardentry/floatbuttonstrategy.h \
|
||||
src/virtualkeyboardentry/virtualkeyboardtrayicon.h
|
||||
|
||||
DISTFILES +=
|
||||
|
|
|
@ -0,0 +1,133 @@
|
|||
#include "virtualkeyboardentry/floatbutton.h"
|
||||
|
||||
#include <QBitmap>
|
||||
#include <QPainter>
|
||||
#include <QVariant>
|
||||
|
||||
FloatButton::FloatButton(MouseClickedCallback mouseClickedCallback)
|
||||
: mouseClickedCallback_(std::move(mouseClickedCallback)) {
|
||||
// 主题框架默认禁用了move消息,因此,QPushButton需要禁用主题框架
|
||||
setProperty("useStyleWindowManager", QVariant(false));
|
||||
initStyle();
|
||||
}
|
||||
|
||||
void FloatButton::move(int x, int y) { QPushButton::move(x, y); }
|
||||
|
||||
void FloatButton::resize(int width, int height) {
|
||||
setFixedSize(width, height);
|
||||
setIconSize(size());
|
||||
updateBorderRadius();
|
||||
QPushButton::resize(width, height);
|
||||
}
|
||||
|
||||
void FloatButton::mousePressEvent(QMouseEvent *event) {
|
||||
if (event->button() == Qt::LeftButton) {
|
||||
startX_ = event->pos().x();
|
||||
startY_ = event->pos().y();
|
||||
|
||||
startClickTimer();
|
||||
}
|
||||
|
||||
QPushButton::mousePressEvent(event);
|
||||
}
|
||||
|
||||
bool FloatButton::shouldPerformMouseClick() const {
|
||||
return !isFloatButtonMoved() && clickTimer_->isActive();
|
||||
}
|
||||
|
||||
void FloatButton::processMouseReleaseEvent() {
|
||||
emit mouseReleased();
|
||||
|
||||
manhattonLength = 0;
|
||||
|
||||
startX_ = -1;
|
||||
startY_ = -1;
|
||||
}
|
||||
|
||||
void FloatButton::processMouseClickEvent() {
|
||||
if (!mouseClickedCallback_) {
|
||||
return;
|
||||
}
|
||||
mouseClickedCallback_();
|
||||
}
|
||||
|
||||
void FloatButton::mouseReleaseEvent(QMouseEvent *event) {
|
||||
QPushButton::mouseReleaseEvent(event);
|
||||
|
||||
if (event->button() != Qt::LeftButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
bool couldPerformMouseClick = shouldPerformMouseClick();
|
||||
stopClickTimer();
|
||||
|
||||
if (couldPerformMouseClick) {
|
||||
processMouseClickEvent();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
if (isFloatButtonMoved()) {
|
||||
processMouseReleaseEvent();
|
||||
}
|
||||
}
|
||||
|
||||
void FloatButton::updateManhattonLength(QMouseEvent *event) {
|
||||
int offsetX = event->pos().x() - startX_;
|
||||
int offsetY = event->pos().y() - startY_;
|
||||
|
||||
manhattonLength += std::abs(offsetX) + std::abs(offsetY);
|
||||
}
|
||||
|
||||
bool FloatButton::isFloatButtonMoved() const {
|
||||
return manhattonLength > manhattonLengthThreshold;
|
||||
}
|
||||
|
||||
void FloatButton::processMouseMoveEvent(QMouseEvent *event) {
|
||||
if (!isFloatButtonMoved()) {
|
||||
updateManhattonLength(event);
|
||||
} else {
|
||||
emit mouseMoved(event->pos().x() - startX_, event->pos().y() - startY_);
|
||||
}
|
||||
}
|
||||
|
||||
void FloatButton::mouseMoveEvent(QMouseEvent *event) {
|
||||
if (event->buttons() & Qt::LeftButton) {
|
||||
processMouseMoveEvent(event);
|
||||
}
|
||||
QPushButton::mouseMoveEvent(event);
|
||||
}
|
||||
|
||||
void FloatButton::initStyle() {
|
||||
setAttribute(Qt::WA_TranslucentBackground);
|
||||
setStyleSheet("QPushButton{border-image: "
|
||||
"url(:/floatbutton/img/floatbuttondefault.svg);}"
|
||||
"QPushButton:hover{border-image: "
|
||||
"url(:/floatbutton/img/floatbuttonhovered.svg);}"
|
||||
"QPushButton:pressed{border-image: "
|
||||
"url(:/floatbutton/img/floatbuttonpressed.svg);}");
|
||||
}
|
||||
|
||||
void FloatButton::updateBorderRadius() {
|
||||
int w = width();
|
||||
int h = height();
|
||||
QBitmap bmp(w, h);
|
||||
bmp.fill();
|
||||
QPainter p(&bmp);
|
||||
p.setPen(Qt::NoPen);
|
||||
p.setBrush(Qt::black);
|
||||
p.drawRoundedRect(bmp.rect(), w, h);
|
||||
setMask(bmp);
|
||||
}
|
||||
|
||||
void FloatButton::startClickTimer() {
|
||||
clickTimer_.reset(new QTimer());
|
||||
clickTimer_->setSingleShot(true);
|
||||
clickTimer_->start(clickTimeThreshold_);
|
||||
}
|
||||
|
||||
void FloatButton::stopClickTimer() {
|
||||
if (clickTimer_ == nullptr) {
|
||||
clickTimer_.reset();
|
||||
}
|
||||
}
|
|
@ -0,0 +1,62 @@
|
|||
#ifndef FLOATBUTTON_H
|
||||
#define FLOATBUTTON_H
|
||||
|
||||
#include <functional>
|
||||
#include <memory>
|
||||
|
||||
#include <QMouseEvent>
|
||||
#include <QPushButton>
|
||||
#include <QSize>
|
||||
#include <QTimer>
|
||||
|
||||
class FloatButton : public QPushButton {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
using MouseClickedCallback = std::function<void()>;
|
||||
|
||||
public:
|
||||
explicit FloatButton(MouseClickedCallback mouseClickedCallback);
|
||||
~FloatButton() override = default;
|
||||
|
||||
signals:
|
||||
void mouseMoved(int x, int y);
|
||||
void mouseReleased();
|
||||
|
||||
public slots:
|
||||
void move(int x, int y);
|
||||
void resize(int width, int height);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) override;
|
||||
void mouseReleaseEvent(QMouseEvent *event) override;
|
||||
void mouseMoveEvent(QMouseEvent *event) override;
|
||||
|
||||
private:
|
||||
bool shouldPerformMouseClick() const;
|
||||
void processMouseReleaseEvent();
|
||||
void processMouseClickEvent();
|
||||
void processMouseMoveEvent(QMouseEvent *event);
|
||||
|
||||
void updateManhattonLength(QMouseEvent *event);
|
||||
bool isFloatButtonMoved() const;
|
||||
|
||||
void initStyle();
|
||||
void updateBorderRadius();
|
||||
|
||||
void startClickTimer();
|
||||
void stopClickTimer();
|
||||
|
||||
private:
|
||||
int startX_ = -1;
|
||||
int startY_ = -1;
|
||||
|
||||
int manhattonLength = 0;
|
||||
constexpr static int manhattonLengthThreshold = 10;
|
||||
|
||||
MouseClickedCallback mouseClickedCallback_;
|
||||
std::unique_ptr<QTimer> clickTimer_ = nullptr;
|
||||
const int clickTimeThreshold_ = 600;
|
||||
};
|
||||
|
||||
#endif // FLOATBUTTON_H
|
|
@ -0,0 +1,162 @@
|
|||
#include "virtualkeyboardentry/floatbuttonmanager.h"
|
||||
|
||||
#include <QEvent>
|
||||
#include <QGuiApplication>
|
||||
#include <QIcon>
|
||||
#include <QMouseEvent>
|
||||
#include <QScreen>
|
||||
#include <QTime>
|
||||
|
||||
#include "geometrymanager/floatgeometrymanager.h"
|
||||
#include "geometrymanager/geometrymanager.h"
|
||||
#include "virtualkeyboardentry/floatbuttonstrategy.h"
|
||||
|
||||
// static
|
||||
const QString floatButtonGroup = "floatButton";
|
||||
// static
|
||||
const QString floatButtonEnabledKey = "floatButtonEnabled";
|
||||
|
||||
FloatButtonManager::FloatButtonManager(
|
||||
const VirtualKeyboardManager &virtualKeyboardManager,
|
||||
const FcitxVirtualKeyboardService &fcitxVirtualKeyboardService)
|
||||
: virtualKeyboardManager_(virtualKeyboardManager),
|
||||
fcitxVirtualKeyboardService_(fcitxVirtualKeyboardService) {
|
||||
initGeometryManager();
|
||||
|
||||
initInternalSignalConnections();
|
||||
initScreenSignalConnections();
|
||||
}
|
||||
|
||||
void FloatButtonManager::loadFloatButtonAvailability() {
|
||||
const bool floatButtonEnabled =
|
||||
floatButtonSettings_
|
||||
.getValue(floatButtonGroup, floatButtonEnabledKey, true)
|
||||
.value<bool>();
|
||||
updateFloatButtonEnabled(floatButtonEnabled);
|
||||
}
|
||||
|
||||
void FloatButtonManager::enableFloatButton() { setFloatButtonEnabled(true); }
|
||||
|
||||
void FloatButtonManager::disableFloatButton() { setFloatButtonEnabled(false); }
|
||||
|
||||
void FloatButtonManager::initGeometryManager() {
|
||||
geometryManager_.reset(new FloatGeometryManager(
|
||||
std::unique_ptr<FloatGeometryManager::Strategy>(
|
||||
new FloatButtonStrategy()),
|
||||
floatButtonSettings_));
|
||||
}
|
||||
|
||||
void FloatButtonManager::initScreenSignalConnections() {
|
||||
connect(QGuiApplication::primaryScreen(), &QScreen::geometryChanged, this,
|
||||
&FloatButtonManager::onScreenResolutionChanged);
|
||||
}
|
||||
|
||||
void FloatButtonManager::initInternalSignalConnections() {
|
||||
connect(this, &FloatButtonManager::floatButtonEnabled, this,
|
||||
&FloatButtonManager::initFloatButton);
|
||||
connect(this, &FloatButtonManager::floatButtonDisabled, this,
|
||||
&FloatButtonManager::destroyFloatButton);
|
||||
}
|
||||
|
||||
void FloatButtonManager::initFloatButton() {
|
||||
createFloatButton();
|
||||
|
||||
connectFloatButtonSignals();
|
||||
|
||||
geometryManager_->updateGeometry();
|
||||
}
|
||||
|
||||
void FloatButtonManager::destroyFloatButton() {
|
||||
if (floatButton_ == nullptr) {
|
||||
return;
|
||||
}
|
||||
|
||||
// 销毁之前必须隐藏,否则会导致虚拟键盘进程
|
||||
// 直接退出
|
||||
floatButton_->hide();
|
||||
|
||||
floatButton_.reset();
|
||||
}
|
||||
|
||||
void FloatButtonManager::onScreenResolutionChanged() {
|
||||
if (!floatButtonEnabled_ || !floatButton_->isVisible()) {
|
||||
return;
|
||||
}
|
||||
|
||||
geometryManager_->updateGeometry();
|
||||
}
|
||||
|
||||
void FloatButtonManager::showFloatButton() {
|
||||
if (!floatButtonEnabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
floatButton_->show();
|
||||
}
|
||||
|
||||
void FloatButtonManager::hideFloatButton() {
|
||||
if (!floatButtonEnabled_) {
|
||||
return;
|
||||
}
|
||||
|
||||
floatButton_->hide();
|
||||
}
|
||||
|
||||
void FloatButtonManager::createFloatButton() {
|
||||
floatButton_.reset(new FloatButton(
|
||||
[this]() { fcitxVirtualKeyboardService_.showVirtualKeyboard(); }));
|
||||
floatButton_->setWindowFlags(Qt::FramelessWindowHint |
|
||||
Qt::BypassWindowManagerHint | Qt::Tool);
|
||||
|
||||
floatButton_->show();
|
||||
}
|
||||
|
||||
void FloatButtonManager::connectFloatButtonSignals() {
|
||||
connect(floatButton_.get(), &FloatButton::mouseMoved,
|
||||
geometryManager_.get(), &FloatGeometryManager::moveBy);
|
||||
connect(floatButton_.get(), &FloatButton::mouseReleased,
|
||||
geometryManager_.get(), &FloatGeometryManager::endDrag);
|
||||
|
||||
connect(geometryManager_.get(), &FloatGeometryManager::viewMoved,
|
||||
floatButton_.get(), &FloatButton::move);
|
||||
connect(geometryManager_.get(), &FloatGeometryManager::viewResized,
|
||||
floatButton_.get(), &FloatButton::resize);
|
||||
|
||||
connect(&virtualKeyboardManager_,
|
||||
&VirtualKeyboardManager::virtualKeyboardVisibiltyChanged,
|
||||
floatButton_.get(), [this](bool visible) {
|
||||
if (visible) {
|
||||
hideFloatButton();
|
||||
} else {
|
||||
showFloatButton();
|
||||
}
|
||||
});
|
||||
|
||||
connect(floatButtonView_.get(), &QPushButton::clicked, this,
|
||||
[this]() { fcitxVirtualKeyboardService_.showVirtualKeyboard(); });
|
||||
}
|
||||
|
||||
void FloatButtonManager::updateFloatButtonEnabled(bool enabled) {
|
||||
floatButtonEnabled_ = enabled;
|
||||
|
||||
if (floatButtonEnabled_) {
|
||||
emit floatButtonEnabled();
|
||||
} else {
|
||||
emit floatButtonDisabled();
|
||||
}
|
||||
}
|
||||
|
||||
void FloatButtonManager::setFloatButtonEnabled(bool enabled) {
|
||||
if (floatButtonEnabled_ == enabled) {
|
||||
return;
|
||||
}
|
||||
|
||||
updateFloatButtonEnabled(enabled);
|
||||
|
||||
saveFloatButtonAvailablity();
|
||||
}
|
||||
|
||||
void FloatButtonManager::saveFloatButtonAvailablity() {
|
||||
floatButtonSettings_.setValue(floatButtonGroup, floatButtonEnabledKey,
|
||||
floatButtonEnabled_);
|
||||
}
|
|
@ -0,0 +1,69 @@
|
|||
#ifndef FLOATBUTTONMANAGER_H
|
||||
#define FLOATBUTTONMANAGER_H
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QDateTime>
|
||||
#include <QObject>
|
||||
#include <QPushButton>
|
||||
|
||||
#include "localsettings/viewlocalsettings.h"
|
||||
#include "virtualkeyboard/virtualkeyboardmanager.h"
|
||||
#include "virtualkeyboardentry/fcitxvirtualkeyboardservice.h"
|
||||
#include "virtualkeyboardentry/floatbutton.h"
|
||||
|
||||
class FloatButtonManager : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
FloatButtonManager(
|
||||
const VirtualKeyboardManager &virtualKeyboardManager,
|
||||
const FcitxVirtualKeyboardService &fcitxVirtualKeyboardService);
|
||||
~FloatButtonManager() override = default;
|
||||
|
||||
void loadFloatButtonAvailability();
|
||||
|
||||
void enableFloatButton();
|
||||
void disableFloatButton();
|
||||
|
||||
signals:
|
||||
void floatButtonEnabled();
|
||||
void floatButtonDisabled();
|
||||
|
||||
private slots:
|
||||
void initFloatButton();
|
||||
void destroyFloatButton();
|
||||
|
||||
void onScreenResolutionChanged();
|
||||
|
||||
private:
|
||||
void initGeometryManager();
|
||||
|
||||
void initInternalSignalConnections();
|
||||
void initScreenSignalConnections();
|
||||
|
||||
void createFloatButton();
|
||||
void connectFloatButtonSignals();
|
||||
|
||||
void showFloatButton();
|
||||
void hideFloatButton();
|
||||
|
||||
void updateFloatButtonEnabled(bool enabled);
|
||||
void setFloatButtonEnabled(bool enabled);
|
||||
|
||||
void saveFloatButtonAvailablity();
|
||||
|
||||
private:
|
||||
bool floatButtonEnabled_ = true;
|
||||
|
||||
ViewLocalSettings floatButtonSettings_{"kylinsoft", "kylin float button"};
|
||||
|
||||
const VirtualKeyboardManager &virtualKeyboardManager_;
|
||||
const FcitxVirtualKeyboardService &fcitxVirtualKeyboardService_;
|
||||
|
||||
std::unique_ptr<FloatButton> floatButton_ = nullptr;
|
||||
|
||||
std::unique_ptr<FloatGeometryManager> geometryManager_ = nullptr;
|
||||
};
|
||||
|
||||
#endif // FLOATBUTTONMANAGER_H
|
|
@ -0,0 +1,42 @@
|
|||
#ifndef FLOATBUTTONSTRATEGY_H
|
||||
#define FLOATBUTTONSTRATEGY_H
|
||||
|
||||
#include "geometrymanager/floatgeometrymanager.h"
|
||||
#include "screenmanager.h"
|
||||
|
||||
class FloatButtonStrategy : public FloatGeometryManager::Strategy {
|
||||
public:
|
||||
FloatButtonStrategy() = default;
|
||||
~FloatButtonStrategy() override = default;
|
||||
|
||||
int getDefaultRightMargin() const override {
|
||||
return getUnitWidth() * defaultRightMarginRatio_;
|
||||
}
|
||||
|
||||
int getDefaultBottomMargin() const override {
|
||||
return getUnitHeight() * defaultBottomMarginRatio_;
|
||||
}
|
||||
|
||||
private:
|
||||
int getUnitWidth() const override {
|
||||
const auto viewPortSize = ScreenManager::getPrimaryScreenSize();
|
||||
|
||||
return std::max(viewPortSize.width(), viewPortSize.height());
|
||||
}
|
||||
|
||||
int getUnitHeight() const override {
|
||||
const auto viewPortSize = ScreenManager::getPrimaryScreenSize();
|
||||
|
||||
return std::max(viewPortSize.width(), viewPortSize.height());
|
||||
}
|
||||
|
||||
float getViewWidthRatio() const override { return 56.0 / 1620.0; }
|
||||
|
||||
float getViewHeightRatio() const override { return 56.0 / 1620.0; }
|
||||
|
||||
private:
|
||||
static constexpr float defaultBottomMarginRatio_ = 0.03f;
|
||||
static constexpr float defaultRightMarginRatio_ = 0.02f;
|
||||
};
|
||||
|
||||
#endif // FLOATBUTTONSTRATEGY_H
|
Loading…
Reference in New Issue