general improvements

1. replace raw char array with QByteArray
2. Register QWindow for Quick module
3. Account for hidden and disable state when hovering controls
4. fix quick title bar label alignment

Signed-off-by: Yuhang Zhao <2546789017@qq.com>
This commit is contained in:
Yuhang Zhao 2022-06-01 11:16:34 +08:00
parent 0cff5ff48e
commit a04fd53a38
7 changed files with 98 additions and 48 deletions

View File

@ -65,7 +65,7 @@ public:
Q_NODISCARD bool isWindowFixedSize() const; Q_NODISCARD bool isWindowFixedSize() const;
void setWindowFixedSize(const bool value); void setWindowFixedSize(const bool value);
void emitSignalForAllInstances(const char *signal); void emitSignalForAllInstances(const QByteArray &signal);
private: private:
Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const; Q_NODISCARD QRect mapItemGeometryToScene(const QQuickItem * const item) const;

View File

@ -61,7 +61,7 @@ public:
Q_NODISCARD bool isWindowFixedSize() const; Q_NODISCARD bool isWindowFixedSize() const;
void setWindowFixedSize(const bool value); void setWindowFixedSize(const bool value);
void emitSignalForAllInstances(const char *signal); void emitSignalForAllInstances(const QByteArray &signal);
private: private:
Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const; Q_NODISCARD QRect mapWidgetGeometryToScene(const QWidget * const widget) const;

View File

@ -34,17 +34,23 @@ using namespace Global;
FRAMELESSHELPER_STRING_CONSTANT2(ConfigFileName, ".framelesshelper.ini") FRAMELESSHELPER_STRING_CONSTANT2(ConfigFileName, ".framelesshelper.ini")
static constexpr const struct static const struct
{ {
const char *env = nullptr; const QByteArray env = {};
const char *ini = nullptr; const QByteArray cfg = {};
} OptionsTable[] = { } OptionsTable[] = {
{"FRAMELESSHELPER_USE_CROSS_PLATFORM_QT_IMPLEMENTATION", "Options/UseCrossPlatformQtImplementation"}, {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_USE_CROSS_PLATFORM_QT_IMPLEMENTATION"),
{"FRAMELESSHELPER_FORCE_HIDE_WINDOW_FRAME_BORDER", "Options/ForceHideWindowFrameBorder"}, FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/UseCrossPlatformQtImplementation")},
{"FRAMELESSHELPER_FORCE_SHOW_WINDOW_FRAME_BORDER", "Options/ForceShowWindowFrameBorder"}, {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_FORCE_HIDE_WINDOW_FRAME_BORDER"),
{"FRAMELESSHELPER_DISABLE_WINDOWS_SNAP_LAYOUTS", "Options/DisableWindowsSnapLayouts"}, FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/ForceHideWindowFrameBorder")},
{"FRAMELESSHELPER_WINDOW_USE_ROUND_CORNERS", "Options/WindowUseRoundCorners"}, {FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_FORCE_SHOW_WINDOW_FRAME_BORDER"),
{"FRAMELESSHELPER_CENTER_WINDOW_BEFORE_SHOW", "Options/CenterWindowBeforeShow"} FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/ForceShowWindowFrameBorder")},
{FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_DISABLE_WINDOWS_SNAP_LAYOUTS"),
FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/DisableWindowsSnapLayouts")},
{FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_WINDOW_USE_ROUND_CORNERS"),
FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/WindowUseRoundCorners")},
{FRAMELESSHELPER_BYTEARRAY_LITERAL("FRAMELESSHELPER_CENTER_WINDOW_BEFORE_SHOW"),
FRAMELESSHELPER_BYTEARRAY_LITERAL("Options/CenterWindowBeforeShow")}
}; };
static constexpr const auto OptionCount = std::size(OptionsTable); static constexpr const auto OptionCount = std::size(OptionsTable);
@ -86,8 +92,9 @@ void FramelessConfig::reload(const bool force)
return new QSettings(appDir.filePath(kConfigFileName), QSettings::IniFormat); return new QSettings(appDir.filePath(kConfigFileName), QSettings::IniFormat);
}()); }());
for (int i = 0; i != OptionCount; ++i) { for (int i = 0; i != OptionCount; ++i) {
const bool on = (qEnvironmentVariableIsSet(OptionsTable[i].env) && (qEnvironmentVariableIntValue(OptionsTable[i].env) > 0)) const bool on = (qEnvironmentVariableIsSet(OptionsTable[i].env.constData())
|| (!configFile.isNull() && configFile->value(QUtf8String(OptionsTable[i].ini), false).toBool()); && (qEnvironmentVariableIntValue(OptionsTable[i].env.constData()) > 0))
|| (!configFile.isNull() && configFile->value(QUtf8String(OptionsTable[i].cfg), false).toBool());
g_data()->options[i] = on; g_data()->options[i] = on;
} }
g_data()->loaded = true; g_data()->loaded = true;

View File

@ -111,7 +111,7 @@ void FramelessQuickHelperPrivate::setTitleBarItem(QQuickItem *value)
return; return;
} }
data->titleBarItem = value; data->titleBarItem = value;
emitSignalForAllInstances("titleBarItemChanged"); emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("titleBarItemChanged"));
} }
void FramelessQuickHelperPrivate::attachToWindow() void FramelessQuickHelperPrivate::attachToWindow()
@ -184,7 +184,7 @@ void FramelessQuickHelperPrivate::attachToWindow()
if (FramelessConfig::instance()->isSet(Option::CenterWindowBeforeShow)) { if (FramelessConfig::instance()->isSet(Option::CenterWindowBeforeShow)) {
moveWindowToDesktopCenter(); moveWindowToDesktopCenter();
} }
emitSignalForAllInstances("ready"); emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("ready"));
}); });
} }
@ -305,7 +305,7 @@ void FramelessQuickHelperPrivate::bringWindowToFront()
window->show(); window->show();
} }
if (window->visibility() == QQuickWindow::Minimized) { if (window->visibility() == QQuickWindow::Minimized) {
window->showNormal(); // ### FIXME window->showNormal(); // ### FIXME: we should not show normal, we should restore the previous state.
} }
window->raise(); window->raise();
window->requestActivate(); window->requestActivate();
@ -353,10 +353,10 @@ void FramelessQuickHelperPrivate::setWindowFixedSize(const bool value)
Q_EMIT q->windowFixedSizeChanged(); Q_EMIT q->windowFixedSizeChanged();
} }
void FramelessQuickHelperPrivate::emitSignalForAllInstances(const char *signal) void FramelessQuickHelperPrivate::emitSignalForAllInstances(const QByteArray &signal)
{ {
Q_ASSERT(signal); Q_ASSERT(!signal.isEmpty());
if (!signal) { if (signal.isEmpty()) {
return; return;
} }
Q_Q(FramelessQuickHelper); Q_Q(FramelessQuickHelper);
@ -370,7 +370,7 @@ void FramelessQuickHelperPrivate::emitSignalForAllInstances(const char *signal)
return; return;
} }
for (auto &&instance : qAsConst(instances)) { for (auto &&instance : qAsConst(instances)) {
QMetaObject::invokeMethod(instance, signal); QMetaObject::invokeMethod(instance, signal.constData());
} }
} }
@ -397,31 +397,31 @@ bool FramelessQuickHelperPrivate::isInSystemButtons(const QPoint &pos, QuickGlob
} }
*button = QuickGlobal::SystemButtonType::Unknown; *button = QuickGlobal::SystemButtonType::Unknown;
const QuickHelperData data = getWindowData(); const QuickHelperData data = getWindowData();
if (data.windowIconButton) { if (data.windowIconButton && data.windowIconButton->isVisible() && data.windowIconButton->isEnabled()) {
if (mapItemGeometryToScene(data.windowIconButton).contains(pos)) { if (mapItemGeometryToScene(data.windowIconButton).contains(pos)) {
*button = QuickGlobal::SystemButtonType::WindowIcon; *button = QuickGlobal::SystemButtonType::WindowIcon;
return true; return true;
} }
} }
if (data.contextHelpButton) { if (data.contextHelpButton && data.contextHelpButton->isVisible() && data.contextHelpButton->isEnabled()) {
if (mapItemGeometryToScene(data.contextHelpButton).contains(pos)) { if (mapItemGeometryToScene(data.contextHelpButton).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Help; *button = QuickGlobal::SystemButtonType::Help;
return true; return true;
} }
} }
if (data.minimizeButton) { if (data.minimizeButton && data.minimizeButton->isVisible() && data.minimizeButton->isEnabled()) {
if (mapItemGeometryToScene(data.minimizeButton).contains(pos)) { if (mapItemGeometryToScene(data.minimizeButton).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Minimize; *button = QuickGlobal::SystemButtonType::Minimize;
return true; return true;
} }
} }
if (data.maximizeButton) { if (data.maximizeButton && data.maximizeButton->isVisible() && data.maximizeButton->isEnabled()) {
if (mapItemGeometryToScene(data.maximizeButton).contains(pos)) { if (mapItemGeometryToScene(data.maximizeButton).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Maximize; *button = QuickGlobal::SystemButtonType::Maximize;
return true; return true;
} }
} }
if (data.closeButton) { if (data.closeButton && data.closeButton->isVisible() && data.closeButton->isEnabled()) {
if (mapItemGeometryToScene(data.closeButton).contains(pos)) { if (mapItemGeometryToScene(data.closeButton).contains(pos)) {
*button = QuickGlobal::SystemButtonType::Close; *button = QuickGlobal::SystemButtonType::Close;
return true; return true;
@ -434,20 +434,38 @@ bool FramelessQuickHelperPrivate::isInTitleBarDraggableArea(const QPoint &pos) c
{ {
const QuickHelperData data = getWindowData(); const QuickHelperData data = getWindowData();
if (!data.titleBarItem) { if (!data.titleBarItem) {
// There's no title bar at all, the mouse will always be in the client area.
return false; return false;
} }
QRegion region = mapItemGeometryToScene(data.titleBarItem); if (!data.titleBarItem->isVisible() || !data.titleBarItem->isEnabled()) {
// The title bar is hidden or disabled for some reason, treat it as there's no title bar.
return false;
}
Q_Q(const FramelessQuickHelper);
const QQuickWindow * const window = q->window();
if (!window) {
// The FramelessQuickHelper item has not been attached to a specific window yet,
// so we assume there's no title bar.
return false;
}
const QRect windowRect = {QPoint(0, 0), window->size()};
const QRect titleBarRect = mapItemGeometryToScene(data.titleBarItem);
if (!titleBarRect.intersects(windowRect)) {
// The title bar is totally outside of the window for some reason,
// also treat it as there's no title bar.
return false;
}
QRegion region = titleBarRect;
const auto systemButtons = {data.windowIconButton, data.contextHelpButton, const auto systemButtons = {data.windowIconButton, data.contextHelpButton,
data.minimizeButton, data.maximizeButton, data.closeButton}; data.minimizeButton, data.maximizeButton, data.closeButton};
for (auto &&button : qAsConst(systemButtons)) { for (auto &&button : qAsConst(systemButtons)) {
if (button) { if (button && button->isVisible() && button->isEnabled()) {
region -= mapItemGeometryToScene(button); region -= mapItemGeometryToScene(button);
} }
} }
if (!data.hitTestVisibleItems.isEmpty()) { if (!data.hitTestVisibleItems.isEmpty()) {
for (auto &&item : qAsConst(data.hitTestVisibleItems)) { for (auto &&item : qAsConst(data.hitTestVisibleItems)) {
Q_ASSERT(item); if (item && item->isVisible() && item->isEnabled()) {
if (item) {
region -= mapItemGeometryToScene(item); region -= mapItemGeometryToScene(item);
} }
} }

View File

@ -69,6 +69,7 @@ void FramelessHelper::Quick::registerTypes(QQmlEngine *engine)
Q_UNUSED(scriptEngine); Q_UNUSED(scriptEngine);
return new FramelessQuickUtils; return new FramelessQuickUtils;
}); });
qmlRegisterRevision<QWindow, 254>(QUICK_URI_FULL);
qmlRegisterRevision<QQuickWindow, 254>(QUICK_URI_FULL); qmlRegisterRevision<QQuickWindow, 254>(QUICK_URI_FULL);
qmlRegisterRevision<QQuickItem, 254>(QUICK_URI_FULL); qmlRegisterRevision<QQuickItem, 254>(QUICK_URI_FULL);
qmlRegisterType<FramelessQuickHelper>(QUICK_URI_EXPAND("FramelessHelper")); qmlRegisterType<FramelessQuickHelper>(QUICK_URI_EXPAND("FramelessHelper"));

View File

@ -57,6 +57,13 @@ void QuickStandardTitleBar::setTitleLabelAlignment(const Qt::Alignment value)
} }
m_labelAlignment = value; m_labelAlignment = value;
QQuickAnchors * const labelAnchors = QQuickItemPrivate::get(m_windowTitleLabel.data())->anchors(); QQuickAnchors * const labelAnchors = QQuickItemPrivate::get(m_windowTitleLabel.data())->anchors();
//labelAnchors->setMargins(0);
labelAnchors->resetFill();
labelAnchors->resetCenterIn();
labelAnchors->resetTop();
labelAnchors->resetBottom();
labelAnchors->resetLeft();
labelAnchors->resetRight();
const QQuickItemPrivate * const titleBarPriv = QQuickItemPrivate::get(this); const QQuickItemPrivate * const titleBarPriv = QQuickItemPrivate::get(this);
if (m_labelAlignment & Qt::AlignTop) { if (m_labelAlignment & Qt::AlignTop) {
labelAnchors->setTop(titleBarPriv->top()); labelAnchors->setTop(titleBarPriv->top());
@ -75,13 +82,13 @@ void QuickStandardTitleBar::setTitleLabelAlignment(const Qt::Alignment value)
labelAnchors->setRightMargin(kDefaultTitleBarContentsMargin); labelAnchors->setRightMargin(kDefaultTitleBarContentsMargin);
} }
if (m_labelAlignment & Qt::AlignVCenter) { if (m_labelAlignment & Qt::AlignVCenter) {
labelAnchors->setTopMargin(0); //labelAnchors->setTopMargin(0);
labelAnchors->setBottomMargin(0); //labelAnchors->setBottomMargin(0);
labelAnchors->setVerticalCenter(titleBarPriv->verticalCenter()); labelAnchors->setVerticalCenter(titleBarPriv->verticalCenter());
} }
if (m_labelAlignment & Qt::AlignHCenter) { if (m_labelAlignment & Qt::AlignHCenter) {
labelAnchors->setLeftMargin(0); //labelAnchors->setLeftMargin(0);
labelAnchors->setRightMargin(0); //labelAnchors->setRightMargin(0);
labelAnchors->setHorizontalCenter(titleBarPriv->horizontalCenter()); labelAnchors->setHorizontalCenter(titleBarPriv->horizontalCenter());
} }
Q_EMIT titleLabelAlignmentChanged(); Q_EMIT titleLabelAlignmentChanged();

View File

@ -130,10 +130,10 @@ void FramelessWidgetsHelperPrivate::setWindowFixedSize(const bool value)
Q_EMIT q->windowFixedSizeChanged(); Q_EMIT q->windowFixedSizeChanged();
} }
void FramelessWidgetsHelperPrivate::emitSignalForAllInstances(const char *signal) void FramelessWidgetsHelperPrivate::emitSignalForAllInstances(const QByteArray &signal)
{ {
Q_ASSERT(signal); Q_ASSERT(!signal.isEmpty());
if (!signal) { if (signal.isEmpty()) {
return; return;
} }
const QWidget * const window = getWindow(); const QWidget * const window = getWindow();
@ -145,7 +145,7 @@ void FramelessWidgetsHelperPrivate::emitSignalForAllInstances(const char *signal
return; return;
} }
for (auto &&instance : qAsConst(instances)) { for (auto &&instance : qAsConst(instances)) {
QMetaObject::invokeMethod(instance, signal); QMetaObject::invokeMethod(instance, signal.constData());
} }
} }
@ -164,7 +164,7 @@ void FramelessWidgetsHelperPrivate::setTitleBarWidget(QWidget *widget)
return; return;
} }
data->titleBarWidget = widget; data->titleBarWidget = widget;
emitSignalForAllInstances("titleBarWidgetChanged"); emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("titleBarWidgetChanged"));
} }
QWidget *FramelessWidgetsHelperPrivate::getTitleBarWidget() const QWidget *FramelessWidgetsHelperPrivate::getTitleBarWidget() const
@ -268,7 +268,7 @@ void FramelessWidgetsHelperPrivate::attachToWindow()
if (FramelessConfig::instance()->isSet(Option::CenterWindowBeforeShow)) { if (FramelessConfig::instance()->isSet(Option::CenterWindowBeforeShow)) {
moveWindowToDesktopCenter(); moveWindowToDesktopCenter();
} }
emitSignalForAllInstances("ready"); emitSignalForAllInstances(FRAMELESSHELPER_BYTEARRAY_LITERAL("ready"));
}); });
} }
@ -338,31 +338,31 @@ bool FramelessWidgetsHelperPrivate::isInSystemButtons(const QPoint &pos, SystemB
} }
*button = SystemButtonType::Unknown; *button = SystemButtonType::Unknown;
const WidgetsHelperData data = getWindowData(); const WidgetsHelperData data = getWindowData();
if (data.windowIconButton) { if (data.windowIconButton && data.windowIconButton->isVisible() && data.windowIconButton->isEnabled()) {
if (data.windowIconButton->geometry().contains(pos)) { if (data.windowIconButton->geometry().contains(pos)) {
*button = SystemButtonType::WindowIcon; *button = SystemButtonType::WindowIcon;
return true; return true;
} }
} }
if (data.contextHelpButton) { if (data.contextHelpButton && data.contextHelpButton->isVisible() && data.contextHelpButton->isEnabled()) {
if (data.contextHelpButton->geometry().contains(pos)) { if (data.contextHelpButton->geometry().contains(pos)) {
*button = SystemButtonType::Help; *button = SystemButtonType::Help;
return true; return true;
} }
} }
if (data.minimizeButton) { if (data.minimizeButton && data.minimizeButton->isVisible() && data.minimizeButton->isEnabled()) {
if (data.minimizeButton->geometry().contains(pos)) { if (data.minimizeButton->geometry().contains(pos)) {
*button = SystemButtonType::Minimize; *button = SystemButtonType::Minimize;
return true; return true;
} }
} }
if (data.maximizeButton) { if (data.maximizeButton && data.maximizeButton->isVisible() && data.maximizeButton->isEnabled()) {
if (data.maximizeButton->geometry().contains(pos)) { if (data.maximizeButton->geometry().contains(pos)) {
*button = SystemButtonType::Maximize; *button = SystemButtonType::Maximize;
return true; return true;
} }
} }
if (data.closeButton) { if (data.closeButton && data.closeButton->isVisible() && data.closeButton->isEnabled()) {
if (data.closeButton->geometry().contains(pos)) { if (data.closeButton->geometry().contains(pos)) {
*button = SystemButtonType::Close; *button = SystemButtonType::Close;
return true; return true;
@ -375,20 +375,37 @@ bool FramelessWidgetsHelperPrivate::isInTitleBarDraggableArea(const QPoint &pos)
{ {
const WidgetsHelperData data = getWindowData(); const WidgetsHelperData data = getWindowData();
if (!data.titleBarWidget) { if (!data.titleBarWidget) {
// There's no title bar at all, the mouse will always be in the client area.
return false; return false;
} }
QRegion region = mapWidgetGeometryToScene(data.titleBarWidget); if (!data.titleBarWidget->isVisible() || !data.titleBarWidget->isEnabled()) {
// The title bar is hidden or disabled for some reason, treat it as there's no title bar.
return false;
}
const QWidget * const window = getWindow();
if (!window) {
// The FramelessWidgetsHelper object has not been attached to a specific window yet,
// so we assume there's no title bar.
return false;
}
const QRect windowRect = {QPoint(0, 0), window->size()};
const QRect titleBarRect = mapWidgetGeometryToScene(data.titleBarWidget);
if (!titleBarRect.intersects(windowRect)) {
// The title bar is totally outside of the window for some reason,
// also treat it as there's no title bar.
return false;
}
QRegion region = titleBarRect;
const auto systemButtons = {data.windowIconButton, data.contextHelpButton, const auto systemButtons = {data.windowIconButton, data.contextHelpButton,
data.minimizeButton, data.maximizeButton, data.closeButton}; data.minimizeButton, data.maximizeButton, data.closeButton};
for (auto &&button : qAsConst(systemButtons)) { for (auto &&button : qAsConst(systemButtons)) {
if (button) { if (button && button->isVisible() && button->isEnabled()) {
region -= mapWidgetGeometryToScene(button); region -= mapWidgetGeometryToScene(button);
} }
} }
if (!data.hitTestVisibleWidgets.isEmpty()) { if (!data.hitTestVisibleWidgets.isEmpty()) {
for (auto &&widget : qAsConst(data.hitTestVisibleWidgets)) { for (auto &&widget : qAsConst(data.hitTestVisibleWidgets)) {
Q_ASSERT(widget); if (widget && widget->isVisible() && widget->isEnabled()) {
if (widget) {
region -= mapWidgetGeometryToScene(widget); region -= mapWidgetGeometryToScene(widget);
} }
} }