1
0
mirror of https://github.com/jaredtao/TaoQuick.git synced 2025-01-29 21:12:55 +08:00

使用简易的无边框窗口,能改变大小和拖动即可。比较麻烦的细节问题不在这里处理了。

This commit is contained in:
jared 2024-12-11 14:47:56 +08:00
parent 0599d5bac8
commit feeeb75d99
11 changed files with 343 additions and 589 deletions

View File

@ -0,0 +1,217 @@
#include "Frameless/TaoFrameLessView.h"
#include <QGuiApplication>
#include <QQuickItem>
#include <QScreen>
#include <QWindow>
struct TaoFrameLessViewPrivate
{
uint32_t mPressedEdge;
QRect mPressedGeometry;
};
TaoFrameLessView::TaoFrameLessView(QWindow* parent)
: Super(parent)
, d(std::make_unique<TaoFrameLessViewPrivate>())
{
setFlags(flags() | Qt::FramelessWindowHint | Qt::Window);
setResizeMode(SizeRootObjectToView);
}
TaoFrameLessView::~TaoFrameLessView() { }
QRect TaoFrameLessView::calcCenterGeo(const QRect& screenGeo, const QSize& normalSize)
{
int w = normalSize.width();
int h = normalSize.height();
int x = screenGeo.x() + (screenGeo.width() - w) / 2;
int y = screenGeo.y() + (screenGeo.height() - h) / 2;
if (screenGeo.width() < w)
{
x = screenGeo.x();
w = screenGeo.width();
}
if (screenGeo.height() < h)
{
y = screenGeo.y();
h = screenGeo.height();
}
return { x, y, w, h };
}
void TaoFrameLessView::moveToScreenCenter()
{
auto geo = calcCenterGeo(screen()->availableGeometry(), size());
if (minimumWidth() > geo.width() || minimumHeight() > geo.height())
{
setMinimumSize(geo.size());
}
setGeometry(geo);
update();
}
void TaoFrameLessView::move(int x, int y)
{
setPosition(x, y);
}
QPoint TaoFrameLessView::mousePosition() const
{
return QCursor::pos();
}
QPoint TaoFrameLessView::globalPosToWindowPos(const QPoint& pos) const
{
return mapFromGlobal(pos);
}
void TaoFrameLessView::mousePressEvent(QMouseEvent* event)
{
auto mousePos = event->pos();
emit mousePressed(mousePos.x(), mousePos.y(), event->button());
d->mPressedEdge = getPosEdges(mousePos);
if (d->mPressedEdge)
{
d->mPressedGeometry = geometry();
}
Super::mousePressEvent(event);
}
void TaoFrameLessView::mouseReleaseEvent(QMouseEvent* event)
{
d->mPressedEdge = 0;
d->mPressedGeometry = {};
Super::mouseReleaseEvent(event);
}
void TaoFrameLessView::mouseMoveEvent(QMouseEvent* event)
{
{
// 实时计算光标
auto edges = getPosEdges(event->pos());
auto shape = getCursorShapeByEdge(edges);
setCursor(shape);
}
{
// 根据按下时位置执行move
if (d->mPressedEdge)
{
doMoveTo(event->pos());
}
}
Super::mouseMoveEvent(event);
}
Qt::CursorShape TaoFrameLessView::getCursorShapeByEdge(const Qt::Edges& edges)
{
switch (edges)
{
case Qt::Edge::TopEdge:
case Qt::Edge::BottomEdge: {
return Qt::CursorShape::SizeVerCursor;
break;
}
case Qt::Edge::LeftEdge:
case Qt::Edge::RightEdge: {
return Qt::CursorShape::SizeHorCursor;
break;
}
case (Qt::Edge::TopEdge | Qt::Edge::LeftEdge):
case (Qt::Edge::BottomEdge | Qt::Edge::RightEdge): {
return Qt::CursorShape::SizeFDiagCursor;
break;
}
case (Qt::Edge::TopEdge | Qt::Edge::RightEdge):
case (Qt::Edge::BottomEdge | Qt::Edge::LeftEdge): {
return Qt::CursorShape::SizeBDiagCursor;
break;
}
default:
break;
}
return Qt::CursorShape::ArrowCursor;
}
void TaoFrameLessView::doMoveTo(const QPoint& nowPos)
{
switch (d->mPressedEdge)
{
case Qt::Edge::TopEdge: {
auto gPos = mapToGlobal(nowPos);
auto newGeo = d->mPressedGeometry.adjusted(0, gPos.y() - d->mPressedGeometry.top(), 0, 0);
// setGeometry(newGeo);
setY(newGeo.y());
setHeight(newGeo.height());
break;
}
case Qt::Edge::BottomEdge: {
auto gPos = mapToGlobal(nowPos);
auto newGeo = d->mPressedGeometry.adjusted(0, 0, 0, gPos.y() - d->mPressedGeometry.bottom());
setGeometry(newGeo);
break;
}
case Qt::Edge::LeftEdge: {
auto gPos = mapToGlobal(nowPos);
auto newGeo = d->mPressedGeometry.adjusted(gPos.x() - d->mPressedGeometry.left(), 0, 0, 0);
setGeometry(newGeo);
break;
}
case Qt::Edge::RightEdge: {
auto gPos = mapToGlobal(nowPos);
auto newGeo = d->mPressedGeometry.adjusted(0, 0, gPos.x() - d->mPressedGeometry.right(), 0);
setGeometry(newGeo);
break;
}
case (Qt::Edge::TopEdge | Qt::Edge::LeftEdge): {
auto gPos = mapToGlobal(nowPos);
auto newGeo = d->mPressedGeometry.adjusted(gPos.x() - d->mPressedGeometry.left(), gPos.y() - d->mPressedGeometry.top(), 0, 0);
setGeometry(newGeo);
break;
}
case (Qt::Edge::BottomEdge | Qt::Edge::RightEdge): {
auto gPos = mapToGlobal(nowPos);
auto newGeo = d->mPressedGeometry.adjusted(0, 0, gPos.x() - d->mPressedGeometry.right(), gPos.y() - d->mPressedGeometry.bottom());
setGeometry(newGeo);
break;
}
case (Qt::Edge::TopEdge | Qt::Edge::RightEdge): {
auto gPos = mapToGlobal(nowPos);
auto newGeo = d->mPressedGeometry.adjusted(0, gPos.y() - d->mPressedGeometry.top(), gPos.x() - d->mPressedGeometry.right(), 0);
setGeometry(newGeo);
break;
}
case (Qt::Edge::BottomEdge | Qt::Edge::LeftEdge): {
auto gPos = mapToGlobal(nowPos);
auto newGeo = d->mPressedGeometry.adjusted(gPos.x() - d->mPressedGeometry.left(), 0, 0, gPos.y() - d->mPressedGeometry.bottom());
setGeometry(newGeo);
break;
}
default:
break;
}
}
Qt::Edges TaoFrameLessView::getPosEdges(const QPoint& pos) const
{
uint32_t edges = 0;
if (pos.x() < 0 || pos.x() > width())
{
return (Qt::Edges)edges;
}
if (pos.y() < 0 || pos.y() > height())
{
return (Qt::Edges)edges;
}
if (pos.x() <= borderWidth())
{
edges |= Qt::Edge::LeftEdge;
}
else if (width() - borderWidth() <= pos.x())
{
edges |= Qt::Edge::RightEdge;
}
if (pos.y() <= borderWidth())
{
edges |= Qt::Edge::TopEdge;
}
else if (height() - borderWidth() <= pos.y())
{
edges |= Qt::Edge::BottomEdge;
}
return (Qt::Edges)edges;
}

View File

@ -1,56 +1,43 @@
#pragma once #pragma once
#include "Common/PropertyHelper.h"
#include "TaoCommonGlobal.h" #include "TaoCommonGlobal.h"
#include <QMouseEvent> #include <QMouseEvent>
#include <QQuickView> #include <QQuickView>
#include <QRegion> #include <QRegion>
// 无边框窗口,主要用来实现自定义标题栏。 // 简易的无边框窗口,主要用来实现自定义标题栏。
// Windows平台支持拖动和改变大小支持Aero效果 // 支持标题栏拖动和边缘改变大小,不做深度处理
// 非Windows平台去掉边框不做其它处理。由Qml模拟resize和拖动。 struct TaoFrameLessViewPrivate;
class TaoFrameLessViewPrivate;
class TAO_API TaoFrameLessView : public QQuickView class TAO_API TaoFrameLessView : public QQuickView
{ {
Q_OBJECT Q_OBJECT
using Super = QQuickView; using Super = QQuickView;
Q_PROPERTY(bool isMax READ isMax NOTIFY isMaxChanged) AUTO_PROPERTY(int, borderWidth, 4)
Q_PROPERTY(bool isFull READ isFull NOTIFY isFullChanged)
public: public:
explicit TaoFrameLessView(QWindow* parent = nullptr); explicit TaoFrameLessView(QWindow* parent = nullptr);
~TaoFrameLessView(); ~TaoFrameLessView();
void moveToScreenCenter();
bool isMax() const;
bool isFull() const;
QQuickItem* titleItem() const;
static QRect calcCenterGeo(const QRect& screenGeo, const QSize& normalSize); static QRect calcCenterGeo(const QRect& screenGeo, const QSize& normalSize);
public slots: public slots:
void setIsMax(bool isMax); void moveToScreenCenter();
void setIsFull(bool isFull); void move(int x, int y);
void setTitleItem(QQuickItem* item);
QPoint mousePosition() const;
QPoint globalPosToWindowPos(const QPoint& pos) const;
signals: signals:
void isMaxChanged(bool isMax);
void isFullChanged(bool isFull);
void mousePressed(int xPos, int yPos, int button); void mousePressed(int xPos, int yPos, int button);
protected: protected:
void showEvent(QShowEvent* e) override; void mousePressEvent(QMouseEvent* event) override;
void resizeEvent(QResizeEvent* e) override; void mouseReleaseEvent(QMouseEvent* event) override;
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) void mouseMoveEvent(QMouseEvent* event) override;
bool nativeEvent(const QByteArray& eventType, void* message, qintptr* result) override;
#else Qt::Edges getPosEdges(const QPoint& pos) const;
bool nativeEvent(const QByteArray& eventType, void* message, long* result) override; Qt::CursorShape getCursorShapeByEdge(const Qt::Edges& edges);
#endif
void mousePressEvent(QMouseEvent* event) override void doMoveTo(const QPoint& nowPos);
{
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
emit mousePressed(event->position().x(), event->position().y(), event->button());
#else
emit mousePressed(event->x(), event->y(), event->button());
#endif
Super::mousePressEvent(event);
}
private: private:
TaoFrameLessViewPrivate* d; std::unique_ptr<TaoFrameLessViewPrivate> d;
}; };

View File

@ -1,111 +0,0 @@
#include "Frameless/TaoFrameLessView.h"
#include <QGuiApplication>
#include <QQuickItem>
#include <QScreen>
#include <QWindow>
class TaoFrameLessViewPrivate
{
public:
bool m_isMax = false;
bool m_isFull = false;
QQuickItem* m_titleItem = nullptr;
};
TaoFrameLessView::TaoFrameLessView(QWindow* parent)
: Super(parent)
, d(new TaoFrameLessViewPrivate)
{
setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint | Qt::WindowSystemMenuHint);
setResizeMode(SizeRootObjectToView);
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
connect(this, &QWindow::windowStateChanged, this, [&](Qt::WindowState state) {
(void)state;
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
});
}
TaoFrameLessView::~TaoFrameLessView()
{
delete d;
}
void TaoFrameLessView::showEvent(QShowEvent* e)
{
Super::showEvent(e);
}
QRect TaoFrameLessView::calcCenterGeo(const QRect& screenGeo, const QSize& normalSize)
{
int w = normalSize.width();
int h = normalSize.height();
int x = screenGeo.x() + (screenGeo.width() - w) / 2;
int y = screenGeo.y() + (screenGeo.height() - h) / 2;
if (screenGeo.width() < w)
{
x = screenGeo.x();
w = screenGeo.width();
}
if (screenGeo.height() < h)
{
y = screenGeo.y();
h = screenGeo.height();
}
return { x, y, w, h };
}
void TaoFrameLessView::moveToScreenCenter()
{
auto geo = calcCenterGeo(screen()->availableGeometry(), size());
if (minimumWidth() > geo.width() || minimumHeight() > geo.height())
{
setMinimumSize(geo.size());
}
setGeometry(geo);
update();
}
bool TaoFrameLessView::isMax() const
{
return d->m_isMax;
}
bool TaoFrameLessView::isFull() const
{
return d->m_isFull;
}
QQuickItem* TaoFrameLessView::titleItem() const
{
return d->m_titleItem;
}
void TaoFrameLessView::setIsMax(bool isMax)
{
if (d->m_isMax == isMax)
return;
d->m_isMax = isMax;
emit isMaxChanged(d->m_isMax);
}
void TaoFrameLessView::setIsFull(bool isFull)
{
if (d->m_isFull == isFull)
return;
d->m_isFull = isFull;
emit isFullChanged(d->m_isFull);
}
void TaoFrameLessView::setTitleItem(QQuickItem* item)
{
d->m_titleItem = item;
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool TaoFrameLessView::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
#else
bool TaoFrameLessView::nativeEvent(const QByteArray& eventType, void* message, long* result)
#endif
{
return Super::nativeEvent(eventType, message, result);
}
void TaoFrameLessView::resizeEvent(QResizeEvent* e)
{
Super::resizeEvent(e);
}

View File

@ -1,363 +0,0 @@
#include "Frameless/TaoFrameLessView.h"
#include <QGuiApplication>
#include <QQuickItem>
#include <QScreen>
#include <QWindow>
#include <windows.h>
#include <VersionHelpers.h>
#include <WinUser.h>
#include <dwmapi.h>
#include <objidl.h> // Fixes error C2504: 'IUnknown' : base class undefined
#include <windowsx.h>
#include <wtypes.h>
#pragma comment(lib, "Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved
#pragma comment(lib, "User32.lib")
#pragma comment(lib, "Gdi32.lib")
// we cannot just use WS_POPUP style
// WS_THICKFRAME: without this the window cannot be resized and so aero snap, de-maximizing and minimizing won't work
// WS_SYSMENU: enables the context menu with the move, close, maximize, minize... commands (shift + right-click on the task bar item)
// WS_CAPTION: enables aero minimize animation/transition
// WS_MAXIMIZEBOX, WS_MINIMIZEBOX: enable minimize/maximize
enum class Style : DWORD
{
windowed = WS_OVERLAPPEDWINDOW | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
aero_borderless = WS_POPUP | WS_THICKFRAME | WS_CAPTION | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX,
basic_borderless = WS_POPUP | WS_THICKFRAME | WS_SYSMENU | WS_MAXIMIZEBOX | WS_MINIMIZEBOX
};
static bool isCompositionEnabled()
{
BOOL composition_enabled = FALSE;
bool success = ::DwmIsCompositionEnabled(&composition_enabled) == S_OK;
return composition_enabled && success;
}
static Style selectBorderLessStyle()
{
return isCompositionEnabled() ? Style::aero_borderless : Style::basic_borderless;
}
static void setShadow(HWND handle, bool enabled)
{
if (isCompositionEnabled())
{
static const MARGINS shadow_state[2] { { 0, 0, 0, 0 }, { 1, 1, 1, 1 } };
::DwmExtendFrameIntoClientArea(handle, &shadow_state[enabled]);
}
}
static long hitTest(RECT winrect, long x, long y, int borderWidth)
{
// 鼠标区域位于窗体边框,进行缩放
if ((x >= winrect.left) && (x < winrect.left + borderWidth) && (y >= winrect.top) && (y < winrect.top + borderWidth))
{
return HTTOPLEFT;
}
else if (x < winrect.right && x >= winrect.right - borderWidth && y >= winrect.top && y < winrect.top + borderWidth)
{
return HTTOPRIGHT;
}
else if (x >= winrect.left && x < winrect.left + borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
return HTBOTTOMLEFT;
}
else if (x < winrect.right && x >= winrect.right - borderWidth && y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
return HTBOTTOMRIGHT;
}
else if (x >= winrect.left && x < winrect.left + borderWidth)
{
return HTLEFT;
}
else if (x < winrect.right && x >= winrect.right - borderWidth)
{
return HTRIGHT;
}
else if (y >= winrect.top && y < winrect.top + borderWidth)
{
return HTTOP;
}
else if (y < winrect.bottom && y >= winrect.bottom - borderWidth)
{
return HTBOTTOM;
}
else
{
return 0;
}
}
static bool isMaxWin(QWindow* win)
{
return win->windowState() == Qt::WindowMaximized;
}
static bool isFullWin(QQuickView* win)
{
return win->windowState() == Qt::WindowFullScreen;
}
class TaoFrameLessViewPrivate
{
public:
bool m_firstRun = true;
bool m_isMax = false;
bool m_isFull = false;
QQuickItem* m_titleItem = nullptr;
HMENU mMenuHandler = NULL;
bool borderless = true; // is the window currently borderless
bool borderless_resize = true; // should the window allow resizing by dragging the borders while borderless
bool borderless_drag = true; // should the window allow moving my dragging the client area
bool borderless_shadow = true; // should the window display a native aero shadow while borderless
void setBorderLess(HWND handle, bool enabled)
{
auto newStyle = enabled ? selectBorderLessStyle() : Style::windowed;
auto oldStyle = static_cast<Style>(::GetWindowLongPtrW(handle, GWL_STYLE));
if (oldStyle != newStyle)
{
borderless = enabled;
::SetWindowLongPtrW(handle, GWL_STYLE, static_cast<LONG>(newStyle));
// when switching between borderless and windowed, restore appropriate shadow state
setShadow(handle, borderless_shadow && (newStyle != Style::windowed));
// redraw frame
::SetWindowPos(handle, nullptr, 0, 0, 0, 0, SWP_FRAMECHANGED | SWP_NOMOVE | SWP_NOSIZE);
::ShowWindow(handle, SW_SHOW);
}
}
void setBorderLessShadow(HWND handle, bool enabled)
{
if (borderless)
{
borderless_shadow = enabled;
setShadow(handle, enabled);
}
}
};
TaoFrameLessView::TaoFrameLessView(QWindow* parent)
: QQuickView(parent)
, d(new TaoFrameLessViewPrivate)
{
// 此处不需要设置flags
// setFlags(Qt::CustomizeWindowHint | Qt::Window | Qt::FramelessWindowHint | Qt::WindowMinMaxButtonsHint | Qt::WindowTitleHint |
// Qt::WindowSystemMenuHint);
setResizeMode(SizeRootObjectToView);
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
connect(this, &QWindow::windowStateChanged, this, [&](Qt::WindowState state) {
(void)state;
setIsMax(windowState() == Qt::WindowMaximized);
setIsFull(windowState() == Qt::WindowFullScreen);
});
}
void TaoFrameLessView::showEvent(QShowEvent* e)
{
if (d->m_firstRun)
{
d->m_firstRun = false;
// 第一次show的时候设置无边框。不在构造函数中设置。取winId会触发QWindowsWindow::create,直接创建win32窗口,引起错乱(win7 或者虚拟机启动即黑屏)。
d->setBorderLess((HWND)(winId()), d->borderless);
{
// Qt 5.15.2 的bug; 问题复现及解决方法当使用WM_NCCALCSIZE 修改非客户区大小后移动窗口到其他屏幕时qwindows.dll 源码 qwindowswindow.cpp:2447
// updateFullFrameMargins() 函数 处会调用qwindowswindow.cpp:2453 的
// calculateFullFrameMargins函数重新获取默认的非客户区大小导致最外层窗口移动屏幕时会触发resize消息引起40像素左右的黑边故此处创建Menu
// 使其调用qwindowswindow.cpp:2451 的 QWindowsContext::forceNcCalcSize() 函数计算非客户区大小
// 已知负面效果: 引入win32 MENU后Qt程序中如果有alt开头的快捷键会不生效被Qt滤掉了需要修改Qt源码
// QWindowsKeyMapper::translateKeyEventInternal 中的
// if (msgType == WM_SYSKEYDOWN && (nModifiers & AltAny) != 0 && GetMenu(msg.hwnd) != nullptr)
// return false;
// 这两行屏蔽掉
d->mMenuHandler = ::CreateMenu();
::SetMenu((HWND)winId(), d->mMenuHandler);
}
}
Super::showEvent(e);
}
TaoFrameLessView::~TaoFrameLessView()
{
if (d->mMenuHandler != NULL)
{
::DestroyMenu(d->mMenuHandler);
}
delete d;
}
bool TaoFrameLessView::isMax() const
{
return d->m_isMax;
}
bool TaoFrameLessView::isFull() const
{
return d->m_isFull;
}
QQuickItem* TaoFrameLessView::titleItem() const
{
return d->m_titleItem;
}
void TaoFrameLessView::setTitleItem(QQuickItem* item)
{
d->m_titleItem = item;
}
QRect TaoFrameLessView::calcCenterGeo(const QRect& screenGeo, const QSize& normalSize)
{
int w = normalSize.width();
int h = normalSize.height();
int x = screenGeo.x() + (screenGeo.width() - w) / 2;
int y = screenGeo.y() + (screenGeo.height() - h) / 2;
if (screenGeo.width() < w)
{
x = screenGeo.x();
w = screenGeo.width();
}
if (screenGeo.height() < h)
{
y = screenGeo.y();
h = screenGeo.height();
}
return { x, y, w, h };
}
void TaoFrameLessView::moveToScreenCenter()
{
auto geo = calcCenterGeo(screen()->availableGeometry(), size());
if (minimumWidth() > geo.width() || minimumHeight() > geo.height())
{
setMinimumSize(geo.size());
}
setGeometry(geo);
update();
}
void TaoFrameLessView::setIsMax(bool isMax)
{
if (d->m_isMax == isMax)
return;
d->m_isMax = isMax;
emit isMaxChanged(d->m_isMax);
}
void TaoFrameLessView::setIsFull(bool isFull)
{
if (d->m_isFull == isFull)
return;
d->m_isFull = isFull;
emit isFullChanged(d->m_isFull);
}
void TaoFrameLessView::resizeEvent(QResizeEvent* e)
{
// SetWindowRgn(HWND(winId()), CreateRoundRectRgn(0, 0, width(), height(), 4, 4), true);
Super::resizeEvent(e);
}
#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)
bool TaoFrameLessView::nativeEvent(const QByteArray& eventType, void* message, qintptr* result)
#else
bool TaoFrameLessView::nativeEvent(const QByteArray& eventType, void* message, long* result)
#endif
{
const long border_width = 4;
if (!result)
{
// 防御式编程
// 一般不会发生这种情况win7一些极端情况会传空指针进来。解决方案是升级驱动、切换到basic主题。
return false;
}
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
// Work-around a bug caused by typo which only exists in Qt 5.11.1
const auto msg = *reinterpret_cast<MSG**>(message);
#else
const auto msg = static_cast<LPMSG>(message);
#endif
if (!msg || !msg->hwnd)
{
return false;
}
switch (msg->message)
{
case WM_NCCALCSIZE: {
#if 1
const auto mode = static_cast<BOOL>(msg->wParam);
const auto clientRect = mode ? &(reinterpret_cast<LPNCCALCSIZE_PARAMS>(msg->lParam)->rgrc[0]) : reinterpret_cast<LPRECT>(msg->lParam);
if (mode == TRUE && d->borderless)
{
*result = WVR_REDRAW;
// 规避 拖动border进行resize时界面闪烁
if (!isMaxWin(this) && !isFullWin(this))
{
if (clientRect->top != 0)
{
clientRect->top -= 0.1;
}
}
else
{
if (clientRect->top != 0)
{
clientRect->top += 0.1;
}
}
return true;
}
#else
*result = 0;
return true;
#endif
break;
}
case WM_NCACTIVATE: {
if (!isCompositionEnabled())
{
// Prevents window frame reappearing on window activation
// in "basic" theme, where no aero shadow is present.
*result = 1;
return true;
}
break;
}
case WM_NCHITTEST: {
if (d->borderless)
{
RECT winrect;
GetWindowRect(HWND(winId()), &winrect);
long x = GET_X_LPARAM(msg->lParam);
long y = GET_Y_LPARAM(msg->lParam);
*result = 0;
if (!isMaxWin(this) && !isFullWin(this))
{ // 非最大化、非全屏时,进行命中测试,处理边框拖拽
*result = hitTest(winrect, x, y, border_width);
if (0 != *result)
{
return true;
}
}
if (d->m_titleItem)
{
auto titlePos = d->m_titleItem->mapToGlobal({ 0, 0 });
titlePos = mapFromGlobal(titlePos.toPoint());
auto titleRect = QRect(titlePos.x(), titlePos.y(), d->m_titleItem->width(), d->m_titleItem->height());
double dpr = qApp->devicePixelRatio();
QPoint pos = mapFromGlobal(QPoint(x / dpr, y / dpr));
if (titleRect.contains(pos))
{
*result = HTCAPTION;
return true;
}
}
return false;
}
break;
} // end case WM_NCHITTEST
}
return Super::nativeEvent(eventType, message, result);
}

View File

@ -34,12 +34,7 @@ SOURCES += \
$$PWD/QuickModel/QuickListModel.cpp \ $$PWD/QuickModel/QuickListModel.cpp \
$$PWD/QuickTree/Model/QuickTreeModel.cpp \ $$PWD/QuickTree/Model/QuickTreeModel.cpp \
$$PWD/Thread/ThreadPool.cpp \ $$PWD/Thread/ThreadPool.cpp \
$$PWD/Trans/Trans.cpp $$PWD/Trans/Trans.cpp \
$$PWD/Frameless/TaoFrameLessView.cpp
win32 {
SOURCES += \
$$PWD/Frameless/TaoFrameLessView_win.cpp
} else {
SOURCES += \
$$PWD/Frameless/TaoFrameLessView_unix.cpp
}

View File

@ -1,6 +1,7 @@
import QtQuick 2.9 import QtQuick 2.9
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import TaoQuick 1.0 import TaoQuick 1.0
import QtQuick.Window 2.0
import "../Biz" import "../Biz"
Rectangle { Rectangle {
@ -21,7 +22,7 @@ Rectangle {
color: "#ffffff" color: "#ffffff"
} }
} }
property bool isMaxed: view.isMax property bool isMaxed: rootView.visibility == Window.Maximized
Row { Row {
id: controlButtons id: controlButtons
objectName: "controlButtonsRow" objectName: "controlButtonsRow"
@ -36,7 +37,7 @@ Rectangle {
btnImgUrl: imgPath + (hovered || pressed ? "Window/minimal_white.png" : "Window/minimal_gray.png") btnImgUrl: imgPath + (hovered || pressed ? "Window/minimal_white.png" : "Window/minimal_gray.png")
tipText: qsTr("minimal") + trans.transString tipText: qsTr("minimal") + trans.transString
onClicked: { onClicked: {
view.showMinimized() rootView.showMinimized()
} }
} }
CusButton_Image { CusButton_Image {
@ -46,7 +47,7 @@ Rectangle {
btnImgUrl: imgPath + (hovered || pressed ? "Window/max_white.png" : "Window/max_gray.png") btnImgUrl: imgPath + (hovered || pressed ? "Window/max_white.png" : "Window/max_gray.png")
tipText: qsTr("maximize") + trans.transString tipText: qsTr("maximize") + trans.transString
onClicked: { onClicked: {
view.showMaximized() rootView.showMaximized()
} }
} }
CusButton_Image { CusButton_Image {
@ -57,14 +58,14 @@ Rectangle {
+ (hovered || pressed ? "Window/normal_white.png" : "Window/normal_gray.png") + (hovered || pressed ? "Window/normal_white.png" : "Window/normal_gray.png")
tipText: qsTr("normal") + trans.transString tipText: qsTr("normal") + trans.transString
onClicked: { onClicked: {
view.showNormal() rootView.showNormal()
} }
} }
CloseBtn { CloseBtn {
width: 24 width: 24
height: 24 height: 24
onClicked: { onClicked: {
view.close() rootView.close()
} }
} }
} }
@ -132,8 +133,31 @@ Rectangle {
topMargin: 4 topMargin: 4
bottom: parent.bottom bottom: parent.bottom
} }
Component.onCompleted: { MouseArea {
view.setTitleItem(blankItem) anchors.fill: parent
onDoubleClicked: {
if (rootView.visibility == Window.Windowed) {
rootView.showMaximized()
} else {
rootView.showNormal()
}
}
property point windowOffsetPos
property point curGlobalPos
onPressed: {
// ,
// c++MouseAreaevent
windowOffsetPos = rootView.globalPosToWindowPos(rootView.mousePosition())
}
onPositionChanged: function(mouse){
if (rootView.visibility == Window.Maximized) {
rootView.showNormal()
}
//
curGlobalPos = rootView.mousePosition()
//
rootView.move(curGlobalPos.x - windowOffsetPos.x, curGlobalPos.y - windowOffsetPos.y)
}
} }
} }
} }

View File

@ -2,14 +2,24 @@ import QtQuick 2.9
import QtQml 2.0 import QtQml 2.0
import QtQuick.Controls 2.2 import QtQuick.Controls 2.2
import TaoQuick 1.0 import TaoQuick 1.0
import QtGraphicalEffects 1.0
import "./Page" import "./Page"
import "./Pane" import "./Pane"
import "./Dialog" import "./Dialog"
//import Qt.labs.platform 1.1 import Qt.labs.platform 1.1
CusBackground { CusBackground {
id: rootBackground id: rootBackground
width: 1440 width: 1440
height: 960 height: 960
// Rectangle {
// id: borderRect
// anchors.fill: parent
// color: "transparent"
// border.color: "#459bac"
// border.width: 2
// visible: false
// }
CusImage { CusImage {
id: bgImg id: bgImg
source: imgPath + "Window/flower.jpg" source: imgPath + "Window/flower.jpg"
@ -17,31 +27,22 @@ CusBackground {
width: parent.width width: parent.width
height: parent.height height: parent.height
opacity: 0.1 opacity: 0.1
// visible: false
}
SystemTrayIcon {
id: sysTray
visible: true
icon.source: imgPath + "logo/milk.png"
menu: Menu {
MenuItem {
text: qsTr("Quit")
onTriggered: {
rootView.close()
}
}
}
} }
// SystemTrayIcon {
// id: sysTray
// visible: true
// icon.source: imgPath + "logo/milk.png"
// menu: Menu {
// MenuItem {
// text: qsTr("Quit")
// onTriggered: {
// Qt.quit()
// }
// }
// }
// }
//Windows use native event for frameless
//Other platform use CusResizeBorder
CusResizeBorder {
id: resizeBorder
visible: Qt.platform.os !== "windows"
borderWidth: 4
enabled: visible
anchors.fill: rootBackground
control: view
}
AboutDialog { AboutDialog {
id: aboutDialog id: aboutDialog
} }
@ -55,12 +56,12 @@ CusBackground {
} }
Item { Item {
id: content id: content
width: parent.width - resizeBorder.borderWidth * 2 width: parent.width - 8
x: resizeBorder.borderWidth x: 8
anchors { anchors {
top: title.bottom top: title.bottom
bottom: parent.bottom bottom: parent.bottom
bottomMargin: resizeBorder.borderWidth bottomMargin: 8
} }
CusFPS { CusFPS {
anchors { anchors {

View File

@ -63,79 +63,83 @@ int main(int argc, char** argv)
AppInfo appInfo; AppInfo appInfo;
QuickTool quickTool; QuickTool quickTool;
TaoFrameLessView view; TaoFrameLessView rootView;
view.setMinimumSize({ 800, 600 }); rootView.setMinimumSize({ 800, 600 });
view.resize(1440, 960); rootView.resize(1440, 960);
trans.beforeUiReady(view.rootContext(), transDir); trans.beforeUiReady(rootView.rootContext(), transDir);
appInfo.beforeUiReady(view.rootContext()); appInfo.beforeUiReady(rootView.rootContext());
view.engine()->addImportPath(qmlPath); rootView.engine()->addImportPath(qmlPath);
#ifdef TaoQuickImportPath #ifdef TaoQuickImportPath
view.engine()->addImportPath(TaoQuickImportPath); rootView.engine()->addImportPath(TaoQuickImportPath);
view.rootContext()->setContextProperty("taoQuickImportPath", TaoQuickImportPath); rootView.rootContext()->setContextProperty("taoQuickImportPath", TaoQuickImportPath);
qWarning() << "TaoQuickImportPath " << TaoQuickImportPath; qWarning() << "TaoQuickImportPath " << TaoQuickImportPath;
#endif #endif
#ifdef TAODEBUG #ifdef TAODEBUG
view.rootContext()->setContextProperty("isDebug", true); rootView.rootContext()->setContextProperty("isDebug", true);
#else #else
view.rootContext()->setContextProperty("isDebug", QVariant(false)); rootView.rootContext()->setContextProperty("isDebug", QVariant(false));
#endif #endif
#if QT_VERSION > QT_VERSION_CHECK(5, 10, 0) #if QT_VERSION > QT_VERSION_CHECK(5, 10, 0)
view.rootContext()->setContextProperty("hasShape", true); rootView.rootContext()->setContextProperty("hasShape", true);
#else #else
view.rootContext()->setContextProperty("hasShape", false); rootView.rootContext()->setContextProperty("hasShape", false);
#endif #endif
#if QT_VERSION > QT_VERSION_CHECK(5, 14, 0) #if QT_VERSION > QT_VERSION_CHECK(5, 14, 0)
view.rootContext()->setContextProperty("comboBoxHasValueRole", true); rootView.rootContext()->setContextProperty("comboBoxHasValueRole", true);
#else #else
view.rootContext()->setContextProperty("comboBoxHasValueRole", false); rootView.rootContext()->setContextProperty("comboBoxHasValueRole", false);
#endif #endif
#if QT_VERSION > QT_VERSION_CHECK(5, 11, 0) #if QT_VERSION > QT_VERSION_CHECK(5, 11, 0)
view.rootContext()->setContextProperty("scrollBarHasMinimumSize", true); rootView.rootContext()->setContextProperty("scrollBarHasMinimumSize", true);
#else #else
view.rootContext()->setContextProperty("scrollBarHasMinimumSize", false); rootView.rootContext()->setContextProperty("scrollBarHasMinimumSize", false);
#endif #endif
view.rootContext()->setContextProperty("appPath", appPath); rootView.rootContext()->setContextProperty("appPath", appPath);
view.rootContext()->setContextProperty("qmlPath", qmlPath); rootView.rootContext()->setContextProperty("qmlPath", qmlPath);
view.rootContext()->setContextProperty("imgPath", imgPath); rootView.rootContext()->setContextProperty("imgPath", imgPath);
view.rootContext()->setContextProperty("contentsPath", contentsPath); rootView.rootContext()->setContextProperty("contentsPath", contentsPath);
view.rootContext()->setContextProperty("view", &view); rootView.rootContext()->setContextProperty("rootView", &rootView);
view.rootContext()->setContextProperty("quickTool", &quickTool); rootView.rootContext()->setContextProperty("quickTool", &quickTool);
DeviceAddModel model; DeviceAddModel model;
view.rootContext()->setContextProperty("deviceAddModel", &model); rootView.rootContext()->setContextProperty("deviceAddModel", &model);
const QUrl url(qmlPath + QStringLiteral("main.qml")); const QUrl url(qmlPath + QStringLiteral("main.qml"));
QObject::connect( QObject::connect(
&view, &rootView,
&QQuickView::statusChanged, &QQuickView::statusChanged,
&view, &rootView,
[&](QQuickView::Status status) [&](QQuickView::Status status)
{ {
if (status == QQuickView::Status::Ready) if (status == QQuickView::Status::Ready)
{ {
trans.afterUiReady(); trans.afterUiReady();
appInfo.afterUiReady(); appInfo.afterUiReady();
quickTool.setRootObjet(view.rootObject()); quickTool.setRootObjet(rootView.rootObject());
} }
}); });
// qml call 'Qt.quit()' will emit engine::quit, here should call qApp->quit // qml call 'Qt.quit()' will emit engine::quit, here should call qApp->quit
QObject::connect(view.engine(), &QQmlEngine::quit, qApp, &QCoreApplication::quit); QObject::connect(rootView.engine(), &QQmlEngine::quit, qApp, &QCoreApplication::quit);
// qml clear content before quit // qml clear content before quit
QObject::connect(qApp, &QGuiApplication::aboutToQuit, qApp, [&view]() QObject::connect(
{ qApp,
view.setSource({}); &QGuiApplication::aboutToQuit,
qInstallMessageHandler(nullptr); qApp,
}); [&rootView]()
{
rootView.setSource({});
qInstallMessageHandler(nullptr);
});
view.setSource(url); rootView.setSource(url);
view.moveToScreenCenter(); rootView.moveToScreenCenter();
view.show(); rootView.show();
return app.exec(); return app.exec();
} }

View File

@ -17,7 +17,7 @@
<QtRcc Include="Qml.qrc" /> <QtRcc Include="Qml.qrc" />
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\Frameless\TaoFrameLessView_win.cpp" /> <ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\Frameless\TaoFrameLessView.cpp" />
<ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\Logger\Logger.cpp" /> <ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\Logger\Logger.cpp" />
<ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\QuickModel\QuickListItemBase.cpp" /> <ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\QuickModel\QuickListItemBase.cpp" />
<ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\QuickModel\QuickListModel.cpp" /> <ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\QuickModel\QuickListModel.cpp" />

View File

@ -80,9 +80,6 @@
<ClCompile Include="Src\DeviceAddTable\DeviceAddModel.cpp"> <ClCompile Include="Src\DeviceAddTable\DeviceAddModel.cpp">
<Filter>Source Files\DeviceAddTable</Filter> <Filter>Source Files\DeviceAddTable</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\Frameless\TaoFrameLessView_win.cpp">
<Filter>TaoCommon\Frameless</Filter>
</ClCompile>
<ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\Logger\Logger.cpp"> <ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\Logger\Logger.cpp">
<Filter>TaoCommon\Logger</Filter> <Filter>TaoCommon\Logger</Filter>
</ClCompile> </ClCompile>
@ -104,6 +101,9 @@
<ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\Trans\Trans.cpp"> <ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\Trans\Trans.cpp">
<Filter>TaoCommon\Trans</Filter> <Filter>TaoCommon\Trans</Filter>
</ClCompile> </ClCompile>
<ClCompile Include="..\..\3rdparty\TaoCommon\src\TaoCommon\Frameless\TaoFrameLessView.cpp">
<Filter>TaoCommon\Frameless</Filter>
</ClCompile>
</ItemGroup> </ItemGroup>
<ItemGroup> <ItemGroup>
<QtMoc Include="Src\AppInfo.h"> <QtMoc Include="Src\AppInfo.h">

View File

@ -4,10 +4,10 @@ import QtQuick.Controls 2.2
Item { Item {
id: clickShow id: clickShow
function onMousePressed(xPos, yPos, button) { function onMousePressed(xPos, yPos, button) {
clickShow.brust(xPos, yPos, button) clickShow.brust(xPos, yPos, button)
} }
Component.onCompleted: { Component.onCompleted: {
view.mousePressed.connect(onMousePressed) rootView.mousePressed.connect(onMousePressed)
} }
Component { Component {
id: brushComp id: brushComp