mirror of
https://github.com/kingToolbox/WindTerm.git
synced 2024-12-26 04:10:08 +08:00
A popup widget.
This commit is contained in:
parent
2e76fa927c
commit
8f73b75e79
@ -36,6 +36,10 @@ A high-performance spin mutex and locker.
|
|||||||
|
|
||||||
A high-performance thread local storage.
|
A high-performance thread local storage.
|
||||||
|
|
||||||
|
## Widgets/PopupWidget.h/cpp
|
||||||
|
|
||||||
|
A popup widget.
|
||||||
|
|
||||||
## Widgets/Scrollbar.h/cpp
|
## Widgets/Scrollbar.h/cpp
|
||||||
|
|
||||||
A scrollbar supports 64-bit ranges.
|
A scrollbar supports 64-bit ranges.
|
||||||
|
226
src/Widgets/PopupWidget.cpp
Normal file
226
src/Widgets/PopupWidget.cpp
Normal file
@ -0,0 +1,226 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020, WindTerm.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include "PopupWidget.h"
|
||||||
|
|
||||||
|
#include <QAction>
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QDesktopWidget>
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QStyle>
|
||||||
|
#include <QToolButton>
|
||||||
|
#include <QVBoxLayout>
|
||||||
|
|
||||||
|
#include <private/qeffects_p.h>
|
||||||
|
|
||||||
|
#define TRIANGLE_HEIGHT 18
|
||||||
|
|
||||||
|
PopupWidget::PopupWidget(QWidget *parent /*= nullptr*/)
|
||||||
|
: QFrame(parent)
|
||||||
|
{
|
||||||
|
setWindowFlags(Qt::Window
|
||||||
|
| Qt::FramelessWindowHint
|
||||||
|
| Qt::WindowStaysOnTopHint
|
||||||
|
| Qt::X11BypassWindowManagerHint
|
||||||
|
| Qt::WindowDoesNotAcceptFocus);
|
||||||
|
|
||||||
|
setAttribute(Qt::WA_ShowWithoutActivating, true);
|
||||||
|
setAttribute(Qt::WA_X11DoNotAcceptFocus, true);
|
||||||
|
setAttribute(Qt::WA_DeleteOnClose, true);
|
||||||
|
setFocusPolicy(Qt::NoFocus);
|
||||||
|
setFrameShape(QFrame::StyledPanel);
|
||||||
|
setMouseTracking(true);
|
||||||
|
createLayout();
|
||||||
|
}
|
||||||
|
|
||||||
|
PopupWidget::~PopupWidget() {
|
||||||
|
if (QAbstractButton *button = dynamic_cast<QAbstractButton *>(parent())) {
|
||||||
|
if (QToolButton *toolButton = dynamic_cast<QToolButton *>(parent())) {
|
||||||
|
if (QAction *action = toolButton->defaultAction()) {
|
||||||
|
action->setChecked(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
button->setChecked(false);
|
||||||
|
}
|
||||||
|
// Kill any running effect
|
||||||
|
qFadeEffect(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopupWidget::createLayout() {
|
||||||
|
QVBoxLayout *vBoxLayout = new QVBoxLayout;
|
||||||
|
vBoxLayout->setSpacing(0);
|
||||||
|
|
||||||
|
setLayout(vBoxLayout);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool PopupWidget::eventFilter(QObject *watched, QEvent *event) {
|
||||||
|
switch (event->type()) {
|
||||||
|
case QEvent::FocusOut:
|
||||||
|
if (QWidget *widget = dynamic_cast<QWidget *>(watched)) {
|
||||||
|
if (isAncestorOf(widget) == false && QApplication::activePopupWidget() == nullptr) {
|
||||||
|
bool hasMenuAncestor = false;
|
||||||
|
|
||||||
|
do {
|
||||||
|
if (widget->inherits("QMenu")) {
|
||||||
|
hasMenuAncestor = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
} while (widget = widget->parentWidget());
|
||||||
|
|
||||||
|
if (hasMenuAncestor == false) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
case QEvent::KeyPress: {
|
||||||
|
if (QKeyEvent *keyEvent = dynamic_cast<QKeyEvent *>(event)) {
|
||||||
|
QWidget *activePopupWidget = QApplication::activePopupWidget();
|
||||||
|
|
||||||
|
if (keyEvent->matches(QKeySequence::Cancel)) {
|
||||||
|
if (activePopupWidget) {
|
||||||
|
activePopupWidget->close();
|
||||||
|
} else {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
} else if (isAncestorOf(QApplication::focusWidget()) == false) {
|
||||||
|
if (activePopupWidget == nullptr) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QEvent::NonClientAreaMouseButtonPress:
|
||||||
|
case QEvent::WindowStateChange:
|
||||||
|
close();
|
||||||
|
break;
|
||||||
|
case QEvent::MouseButtonPress: {
|
||||||
|
if (QMouseEvent *mouseEvent = dynamic_cast<QMouseEvent *>(event)) {
|
||||||
|
do {
|
||||||
|
QPoint globalPos = mouseEvent->globalPos();
|
||||||
|
|
||||||
|
if (rect().contains(mapFromGlobal(globalPos)))
|
||||||
|
break;
|
||||||
|
|
||||||
|
if (QAbstractButton *button = dynamic_cast<QAbstractButton *>(parentWidget())) {
|
||||||
|
if (button->rect().contains(button->mapFromGlobal(globalPos))) {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
setAttribute(Qt::WA_NoMouseReplay);
|
||||||
|
close();
|
||||||
|
} while (0);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case QEvent::WindowDeactivate: {
|
||||||
|
if (QWidget *parentWidget = this->parentWidget()) {
|
||||||
|
if (parentWidget->isAncestorOf(QApplication::focusWidget()) == false) {
|
||||||
|
close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopupWidget::mousePressEvent(QMouseEvent *event) {
|
||||||
|
setAttribute(Qt::WA_NoMouseReplay);
|
||||||
|
QWidget::mousePressEvent(event);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopupWidget::setCentralWidget(QWidget *widget) {
|
||||||
|
widget->layout()->setContentsMargins(QMargins());
|
||||||
|
widget->setParent(this);
|
||||||
|
layout()->addWidget(widget);
|
||||||
|
adjustSize();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PopupWidget::show(Area hArea, Area vArea) {
|
||||||
|
Q_ASSERT(parentWidget() != nullptr);
|
||||||
|
|
||||||
|
if (QWidget *parent = parentWidget()) {
|
||||||
|
QPoint newPos;
|
||||||
|
QPolygon triPolygon;
|
||||||
|
QRect rectPolygon;
|
||||||
|
|
||||||
|
QPoint pos = parent->mapToGlobal(QPoint());
|
||||||
|
QRect rect = parent->rect();
|
||||||
|
QRect screenRect = QApplication::desktop()->availableGeometry(this);
|
||||||
|
|
||||||
|
int topMargin = style()->pixelMetric(QStyle::PM_LayoutTopMargin);
|
||||||
|
int bottomMargin = style()->pixelMetric(QStyle::PM_LayoutBottomMargin);
|
||||||
|
int leftMargin = style()->pixelMetric(QStyle::PM_LayoutLeftMargin);
|
||||||
|
int rightMargin = style()->pixelMetric(QStyle::PM_LayoutRightMargin);
|
||||||
|
int triangleHeight = std::max(TRIANGLE_HEIGHT, topMargin);
|
||||||
|
|
||||||
|
if (pos.y() < height()) {
|
||||||
|
if (vArea == TopArea) {
|
||||||
|
vArea = BottomArea;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (vArea == BottomArea && screenRect.height() - pos.y() - rect.height() < height()) {
|
||||||
|
vArea = TopArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hArea == MiddleArea) {
|
||||||
|
newPos.setX(qBound(0, pos.x() + (rect.width() - width()) / 2, screenRect.width() - width()));
|
||||||
|
} else {
|
||||||
|
if (hArea == LeftArea) {
|
||||||
|
if (pos.x() + rect.width() < width()) {
|
||||||
|
hArea = RightArea;
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (screenRect.width() - pos.x() < width()) {
|
||||||
|
hArea = LeftArea;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
newPos.setX((hArea == LeftArea) ? pos.x() + rect.width() - width() : pos.x());
|
||||||
|
}
|
||||||
|
newPos.setY((vArea == BottomArea) ? pos.y() + rect.height() : pos.y() - height() - triangleHeight);
|
||||||
|
|
||||||
|
int xCenter = pos.x() + (rect.width() / 2) - newPos.x();
|
||||||
|
|
||||||
|
if (vArea == TopArea) {
|
||||||
|
triPolygon << QPoint(xCenter - triangleHeight, height())
|
||||||
|
<< QPoint(xCenter, height() + triangleHeight)
|
||||||
|
<< QPoint(xCenter + triangleHeight, height());
|
||||||
|
rectPolygon = QRect(0, 0, width(), height());
|
||||||
|
layout()->setContentsMargins(leftMargin, topMargin, rightMargin, bottomMargin + triangleHeight * 2);
|
||||||
|
} else {
|
||||||
|
triPolygon << QPoint(xCenter - triangleHeight, triangleHeight)
|
||||||
|
<< QPoint(xCenter, 0)
|
||||||
|
<< QPoint(xCenter + triangleHeight, triangleHeight);
|
||||||
|
rectPolygon = QRect(0, triangleHeight, width(), height());
|
||||||
|
layout()->setContentsMargins(leftMargin, topMargin + triangleHeight, rightMargin, bottomMargin);
|
||||||
|
}
|
||||||
|
|
||||||
|
QRegion triangle(triPolygon);
|
||||||
|
QRegion rectangle(rectPolygon, QRegion::Rectangle);
|
||||||
|
QRegion mask = rectangle.united(triangle);
|
||||||
|
setMask(mask);
|
||||||
|
|
||||||
|
move(newPos);
|
||||||
|
QWidget::show();
|
||||||
|
|
||||||
|
qFadeEffect(this, 200);
|
||||||
|
qApp->installEventFilter(this);
|
||||||
|
}
|
||||||
|
}
|
47
src/Widgets/PopupWidget.h
Normal file
47
src/Widgets/PopupWidget.h
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
/*
|
||||||
|
* Copyright 2020, WindTerm.
|
||||||
|
*
|
||||||
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
* you may not use this file except in compliance with the License.
|
||||||
|
* You may obtain a copy of the License at
|
||||||
|
*
|
||||||
|
* http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
*
|
||||||
|
* Unless required by applicable law or agreed to in writing, software
|
||||||
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
* See the License for the specific language governing permissions and
|
||||||
|
* limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef POPUPWIDGET_H
|
||||||
|
#define POPUPWIDGET_H
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <QFrame>
|
||||||
|
|
||||||
|
class PopupWidget : public QFrame
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit PopupWidget(QWidget *parent = nullptr);
|
||||||
|
virtual ~PopupWidget();
|
||||||
|
|
||||||
|
enum Area { LeftArea, MiddleArea, RightArea, TopArea, BottomArea };
|
||||||
|
|
||||||
|
public:
|
||||||
|
void setCentralWidget(QWidget *widget);
|
||||||
|
void show(Area hArea, Area vArea);
|
||||||
|
|
||||||
|
private:
|
||||||
|
void createLayout();
|
||||||
|
bool eventFilter(QObject *watched, QEvent *event) override;
|
||||||
|
void mousePressEvent(QMouseEvent *event) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Q_DISABLE_COPY(PopupWidget)
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // POPUPWIDGET_H
|
Loading…
Reference in New Issue
Block a user