Add the shadow layer on windows

This commit is contained in:
dreamsourcelabTAI 2024-03-15 15:12:01 +08:00
parent abe9e8f17b
commit 3e8e7c7807
8 changed files with 1069 additions and 212 deletions

View File

@ -116,7 +116,7 @@ public:
};
enum ParentNativeEvent{
EV_SCREEN_DPI_CHANGED = 0,
PARENT_EVENT_DISPLAY_CHANGED = 0,
};
class IParentNativeEventCallback

View File

@ -45,6 +45,7 @@
#include <QGuiApplication>
#include <QFont>
#include <algorithm>
#include <QWindow>
#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
#include <QDesktopWidget>
@ -77,29 +78,41 @@ MainFrame::MainFrame()
_is_native_title = false;
_is_resize_ready = false;
_parentNativeWidget = NULL;
_mainWindow = NULL;
_is_max_status = false;
_move_start_screen = NULL;
_mainWindow = NULL;
_move_start_screen = NULL;
_left = NULL;
_right = NULL;
_top = NULL;
_bottom = NULL;
_top_left = NULL;
_top_right = NULL;
_bottom_left = NULL;
_bottom_right = NULL;
AppControl::Instance()->SetTopWindow(this);
// Make this a borderless window which can't
// be resized or moved via the window system
bool isWin32 = false;
#ifdef _WIN32
setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint | Qt::WindowMinMaxButtonsHint);
_is_native_title = true;
_taskBtn = NULL;
isWin32 = true;
#else
setWindowFlags(Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
setAttribute(Qt::WA_TranslucentBackground);
_is_native_title = false;
#endif
// setMinimumWidth(MainWindow::Min_Width);
// setMinimumHeight(MainWindow::Min_Height);
setMinimumWidth(300);
setMinimumHeight(400);
#ifdef _WIN32
if (!_is_native_title){
setAttribute(Qt::WA_TranslucentBackground);
}
#endif
setMinimumWidth(MainWindow::Min_Width);
setMinimumHeight(MainWindow::Min_Height);
// Set the window icon
QIcon icon;
@ -119,8 +132,9 @@ MainFrame::MainFrame()
_layout = new QGridLayout(this);
_layout->setSpacing(0);
_layout->setContentsMargins(0,0,0,0);
if (true)
if (!isWin32 || !_is_native_title)
{
_top_left = new widgets::Border (TopLeft, this);
_top_left->setFixedSize(Margin, Margin);
@ -159,6 +173,9 @@ MainFrame::MainFrame()
_layout->addWidget(_bottom, 2, 1);
_layout->addWidget(_bottom_right, 2, 2);
}
else{
_layout->addLayout(vbox, 0, 0);
}
#ifdef _WIN32
_taskBtn = new QWinTaskbarButton(this);
@ -175,82 +192,9 @@ MainFrame::MainFrame()
connect(this, SIGNAL(sig_ParentNativeEvent(int)), this, SLOT(OnParentNaitveWindowEvent(int)));
//PrintRegionProc();
}
void MainFrame::PrintRegionProc()
{
PrintRegion();
QTimer::singleShot(4000, this, [this](){
PrintRegionProc();
});
}
void MainFrame::PrintRegion()
{
QRect rc = geometry();
int x = rc.left();
int y = rc.top();
int w = rc.width();
int h = rc.height();
dsv_info("print region, x:%d, y:%d, w:%d, h:%d",
x, y, w, h);
}
void MainFrame::AttachNativeWindow()
{
#ifdef _WIN32
int k = QApplication::desktop()->screen()->devicePixelRatio();
int x = _initWndInfo.r.x;
int y = _initWndInfo.r.y;
int w = _initWndInfo.r.w;
int h = _initWndInfo.r.h;
x *= k;
y *= k;
w *= k;
h *= k;
if (_parentNativeWidget != NULL){
_parentNativeWidget->SetChildWidget(NULL);
delete _parentNativeWidget;
}
_parentNativeWidget = new WinNativeWidget(x, y, w, h);
_parentNativeWidget->setGeometry(x, y, w, h);
_parentNativeWidget->SetChildWidget(this);
_parentNativeWidget->SetNativeEventCallback(this);
if (_parentNativeWidget->Handle())
{
_parentNativeWidget->Show(true);
setWindowFlags(Qt::FramelessWindowHint);
setProperty("_q_embedded_native_parent_handle", (WId)_parentNativeWidget->Handle());
SetWindowLong((HWND)winId(), GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
SetParent((HWND)winId(), _parentNativeWidget->Handle());
QEvent e(QEvent::EmbeddingControl);
QApplication::sendEvent(this, &e);
QTimer::singleShot(10, this, [this](){
move(0,0);
setVisible(true);
if (_initWndInfo.isMaxSize){
showMaximized();
}
_parentNativeWidget->ResizeChild();
});
}
#endif
}
void MainFrame::MoveWindow(int x, int y)
{
#ifdef _WIN32
@ -280,7 +224,7 @@ QPoint MainFrame::GetParentPos()
bool MainFrame::ParentIsMaxsized()
{
return _is_max_status;
return IsMaxsized();
}
void MainFrame::MoveBegin()
@ -308,11 +252,13 @@ void MainFrame::MoveEnd()
#ifdef _WIN32
if (_parentNativeWidget != NULL){
auto scr = _parentNativeWidget->GetPointScreen();
if (scr != _move_start_screen){
_parentNativeWidget->UpdateChildDpi();
if (scr != _move_start_screen){
_parentNativeWidget->UpdateChildDpi();
_parentNativeWidget->ResizeSelf();
}
_parentNativeWidget->ResizeChild();
_parentNativeWidget->MoveShadow();
_parentNativeWidget->SetMovingFlag(false);
}
#endif
@ -327,17 +273,24 @@ void MainFrame::OnParentNaitveWindowEvent(int msg)
{
#ifdef _WIN32
if (_parentNativeWidget != NULL){
if (msg == EV_SCREEN_DPI_CHANGED){
if (msg == PARENT_EVENT_DISPLAY_CHANGED){
QTimer::singleShot(100, this, [this](){
_parentNativeWidget->UpdateChildDpi();
_parentNativeWidget->ResizeChild();
PopupDlgList::TryCloseAllByScreenChanged();
_parentNativeWidget->OnDisplayChanged();
});
}
}
#endif
}
void MainFrame::OnMoveWinShadow()
{
#ifdef _WIN32
if (_parentNativeWidget != NULL){
_parentNativeWidget->MoveShadow();
}
#endif
}
void MainFrame::resizeEvent(QResizeEvent *event)
{
@ -347,14 +300,14 @@ void MainFrame::resizeEvent(QResizeEvent *event)
return;
}
if (_is_max_status) {
if (IsMaxsized()) {
hide_border();
}
else {
else if(IsNormalsized()) {
show_border();
}
_titleBar->setRestoreButton(_is_max_status);
_titleBar->setRestoreButton(IsMaxsized());
_layout->update();
}
@ -385,6 +338,9 @@ void MainFrame::unfreezing()
void MainFrame::hide_border()
{
if (_top_left == NULL)
return;
_top_left->setVisible(false);
_top_right->setVisible(false);
_top->setVisible(false);
@ -397,6 +353,9 @@ void MainFrame::hide_border()
void MainFrame::show_border()
{
if (_top_left == NULL)
return;
_top_left->setVisible(true);
_top_right->setVisible(true);
_top->setVisible(true);
@ -409,16 +368,11 @@ void MainFrame::show_border()
void MainFrame::showNormal()
{
show_border();
dsv_info("Show as normal.");
_is_max_status = false;
show_border();
#ifdef _WIN32
if (_parentNativeWidget){
_parentNativeWidget->ShowNormal();
_parentNativeWidget->ShowNormal();
return;
}
#endif
@ -429,11 +383,7 @@ void MainFrame::showNormal()
void MainFrame::showMaximized()
{
hide_border();
dsv_info("Show as maxsize.");
_is_max_status = true;
#ifdef _WIN32
if (_parentNativeWidget){
_parentNativeWidget->ShowMax();
@ -445,9 +395,7 @@ void MainFrame::showMaximized()
}
void MainFrame::showMinimized()
{
dsv_info("Show as minsize.");
{
#ifdef _WIN32
if (_parentNativeWidget){
_parentNativeWidget->ShowMin();
@ -471,23 +419,6 @@ void MainFrame::changeEvent(QEvent *event)
QFrame::changeEvent(event);
}
void MainFrame::moveToWinNaitiveNormal()
{
#ifdef _WIN32
if (_parentNativeWidget != NULL){
int k = QApplication::desktop()->screen()->devicePixelRatio();
int x = _normalRegion.x * k;
int y = _normalRegion.y * k;
int w = _normalRegion.w * k;
int h = _normalRegion.h * k;
_parentNativeWidget->setGeometry(x, y, w, h);
}
#endif
}
bool MainFrame::eventFilter(QObject *object, QEvent *event)
{
const QEvent::Type type = event->type();
@ -505,7 +436,7 @@ bool MainFrame::eventFilter(QObject *object, QEvent *event)
}
//when window is maximized, or is moving, call return
if (_is_max_status || _titleBar->IsMoving()){
if (IsMaxsized() || IsMoving()){
return QFrame::eventFilter(object, event);
}
@ -545,17 +476,27 @@ bool MainFrame::eventFilter(QObject *object, QEvent *event)
if (type == QEvent::MouseMove) {
QPoint pt;
int k = 1;
#ifdef _WIN32
POINT p;
GetCursorPos(&p);
pt.setX(p.x);
pt.setY(p.y);
if (_parentNativeWidget != NULL){
auto scr = _parentNativeWidget->GetPointScreen();
assert(scr);
k = scr->devicePixelRatio();
}
#else
pt = mouse_event->globalPos();
#endif
int datX = pt.x() - _clickPos.x();
int datY = pt.y() - _clickPos.y();
datX /= k;
datY /= k;
int l = _dragStartRegion.left();
int t = _dragStartRegion.top();
int r = _dragStartRegion.right();
@ -673,7 +614,6 @@ bool MainFrame::eventFilter(QObject *object, QEvent *event)
setCursor(Qt::ArrowCursor);
}
return QFrame::eventFilter(object, event);
}
@ -682,7 +622,7 @@ void MainFrame::saveNormalRegion()
if (!_is_resize_ready){
return;
}
if (_is_max_status){
if (!IsNormalsized()){
return;
}
@ -691,7 +631,9 @@ void MainFrame::saveNormalRegion()
#ifdef _WIN32
if (_parentNativeWidget != NULL){
RECT rc;
int k = QApplication::desktop()->screen()->devicePixelRatio();
auto scr = _parentNativeWidget->GetPointScreen();
assert(scr);
int k = scr->devicePixelRatio();
GetWindowRect(_parentNativeWidget->Handle(), &rc);
app.frameOptions.left = rc.left / k;
@ -717,9 +659,9 @@ void MainFrame::saveNormalRegion()
void MainFrame::writeSettings()
{
AppConfig &app = AppConfig::Instance();
app.frameOptions.isMax = _is_max_status;
app.frameOptions.isMax = IsMaxsized();
if (_is_max_status == false && isVisible()){
if (IsNormalsized() && isVisible()){
saveNormalRegion();
}
app.SaveFrame();
@ -752,23 +694,18 @@ void MainFrame::ShowFormInit()
h++;
}
dsv_info("Init form, x:%d, y:%d, w:%d, h:%d, k:%d",
x, y, w, h, _initWndInfo.k);
bool isWin32 = false;
#ifdef _WIN32
isWin32 = true;
#endif
if (_initWndInfo.isMaxSize)
{
if (isWin32 && _is_native_title){
hide_border();
move(_normalRegion.x, _normalRegion.y);
resize(_normalRegion.w, _normalRegion.h);
}
else{
move(x, y);
showMaximized();
}
if (_initWndInfo.isMaxSize){
move(x, y);
showMaximized();
}
else{
move(x, y);
@ -776,12 +713,96 @@ void MainFrame::ShowFormInit()
}
QFrame::show();
dsv_info("Init form, x:%d, y:%d, w:%d, h:%d", x, y, w, h);
#ifdef _WIN32
if (_is_native_title){
AttachNativeWindow();
}
}
#endif
}
void MainFrame::AttachNativeWindow()
{
#ifdef _WIN32
assert(_parentNativeWidget == NULL);
int k = _initWndInfo.k;
int x = _normalRegion.x;
int y = _normalRegion.y;
int w = _normalRegion.w;
int h = _normalRegion.h;
x *= k;
y *= k;
w *= k;
h *= k;
WinNativeWidget *nativeWindow = new WinNativeWidget(x, y, w, h);
nativeWindow->setGeometry(x, y, w, h);
if (nativeWindow->Handle() == NULL){
dsv_info("ERROR: native window is invalid.");
return;
}
//check the normal region
QScreen *scr = nativeWindow->GetPointScreen();
if (scr != NULL){
QRect full_rc = scr->availableGeometry();
if (full_rc.width() - _normalRegion.w < 100 && !_initWndInfo.isMaxSize)
{
int w1 = full_rc.width() / 1.5;
int h1 = full_rc.height() / 1.5;
int x1 = full_rc.left() + (full_rc.width() - w1) / 2;
int y1 = full_rc.top() + (full_rc.height() - h1) / 2;
nativeWindow->setGeometry(x1 * k, y1 * k, w1 * k, h1 *k);
dsv_info("%s:Reset the normal region, x:%d, y:%d, w:%d, h:%d",
scr->name().toStdString().c_str(),
x1, y1, w1, h1);
}
}
else{
dsv_info("ERROR: get point screen error.");
}
setWindowFlags(Qt::FramelessWindowHint);
setProperty("_q_embedded_native_parent_handle", (WId)nativeWindow->Handle());
SetWindowLong((HWND)winId(), GWL_STYLE, WS_CHILD | WS_CLIPCHILDREN | WS_CLIPSIBLINGS);
SetParent((HWND)winId(), nativeWindow->Handle());
QEvent e(QEvent::EmbeddingControl);
QApplication::sendEvent(this, &e);
if (_initWndInfo.isMaxSize){
nativeWindow->ShowMax();
}
else{
nativeWindow->Show(true);
}
nativeWindow->SetChildWidget(this);
nativeWindow->SetNativeEventCallback(this);
_parentNativeWidget = nativeWindow;
auto shandow = _parentNativeWidget->Shadow();
if (shandow){
shandow->SetFrame(this);
connect(shandow, SIGNAL(showSignal()), this, SLOT(OnMoveWinShadow()));
}
QTimer::singleShot(50, this, [this](){
move(0,0);
setVisible(true);
_parentNativeWidget->UpdateChildDpi();
_parentNativeWidget->ResizeChild();
_parentNativeWidget->BeginShowShadow();
});
#endif
}
void MainFrame::SetFormRegion(int x, int y, int w, int h)
@ -789,7 +810,9 @@ void MainFrame::SetFormRegion(int x, int y, int w, int h)
#ifdef _WIN32
if (_parentNativeWidget != NULL){
int k = QApplication::desktop()->screen()->devicePixelRatio();
auto scr = _parentNativeWidget->GetPointScreen();
assert(scr);
int k = scr->devicePixelRatio();
x *= k;
y *= k;
@ -812,7 +835,9 @@ QRect MainFrame::GetFormRegion()
#ifdef _WIN32
if (_parentNativeWidget != NULL){
int k = QApplication::desktop()->screen()->devicePixelRatio();
auto scr = _parentNativeWidget->GetPointScreen();
assert(scr);
int k = scr->devicePixelRatio();
RECT r;
GetWindowRect(_parentNativeWidget->Handle(), &r);
int x = r.left;
@ -831,6 +856,49 @@ QRect MainFrame::GetFormRegion()
return rc;
}
bool MainFrame::IsMaxsized()
{
#ifdef _WIN32
if (_parentNativeWidget != NULL){
return _parentNativeWidget->IsMaxsized();
}
#endif
return QFrame::isMaximized();
}
bool MainFrame::IsNormalsized()
{
#ifdef _WIN32
if (_parentNativeWidget != NULL){
return _parentNativeWidget->IsNormalsized();
}
#endif
if (!QFrame::isMaximized() && !QFrame::isMinimized()){
return true;
}
return false;
}
bool MainFrame::IsMoving()
{
return _titleBar->IsMoving();
}
int MainFrame::GetDevicePixelRatio()
{
#ifdef _WIN32
if (_parentNativeWidget != NULL){
auto scr = _parentNativeWidget->GetPointScreen();
assert(scr);
return scr->devicePixelRatio();
}
#endif
return windowHandle()->devicePixelRatio();
}
void MainFrame::ReadSettings()
{
AppConfig &app = AppConfig::Instance();
@ -851,8 +919,8 @@ void MainFrame::ReadSettings()
y = top;
}
dsv_info("Loaded region, x:%d, y:%d, w:%d, h:%d",
x, y, right-left, bottom-top);
dsv_info("Read region info, x:%d, y:%d, w:%d, h:%d, isMax:%d",
x, y, right-left, bottom-top, app.frameOptions.isMax);
bool bReset = false;
int scrIndex = -1;
@ -888,6 +956,7 @@ void MainFrame::ReadSettings()
_initWndInfo.r.w = 0;
_initWndInfo.r.h = 0;
_initWndInfo.isMaxSize = false;
int k = 1;
if (app.frameOptions.isMax)
{
@ -897,10 +966,12 @@ void MainFrame::ReadSettings()
if (scrIndex == -1){
rc = QGuiApplication::primaryScreen()->availableGeometry();
scrName = QGuiApplication::primaryScreen()->name();
k = QGuiApplication::primaryScreen()->devicePixelRatio();
}
else{
rc = QGuiApplication::screens().at(scrIndex)->availableGeometry();
scrName = QGuiApplication::screens().at(scrIndex)->name();
k = QGuiApplication::screens().at(scrIndex)->devicePixelRatio();
}
_initWndInfo.r.x = rc.left();
@ -931,10 +1002,12 @@ void MainFrame::ReadSettings()
if (scrIndex == -1){
rc = QGuiApplication::primaryScreen()->availableGeometry();
scrName = QGuiApplication::primaryScreen()->name();
k = QGuiApplication::primaryScreen()->devicePixelRatio();
}
else{
rc = QGuiApplication::screens().at(scrIndex)->availableGeometry();
scrName = QGuiApplication::screens().at(scrIndex)->name();
k = QGuiApplication::screens().at(scrIndex)->devicePixelRatio();
}
int w = rc.width() / 1.5;
@ -971,6 +1044,7 @@ void MainFrame::ReadSettings()
_normalRegion = _initWndInfo.r;
QString scrName = QGuiApplication::screens().at(scrIndex)->name();
QRect rc = QGuiApplication::screens().at(scrIndex)->availableGeometry();
k = QGuiApplication::screens().at(scrIndex)->devicePixelRatio();
dsv_info("Restore, screen:%s, x:%d, y:%d, w:%d, h:%d",
scrName.toStdString().c_str() ,rc.left(), rc.top(), rc.width(), rc.height());
@ -982,6 +1056,7 @@ void MainFrame::ReadSettings()
// restore dockwidgets
_mainWindow->restore_dock();
_titleBar->setRestoreButton(app.frameOptions.isMax);
_initWndInfo.k = k;
}
#ifdef _WIN32

View File

@ -64,6 +64,7 @@ struct FormInitInfo
{
FormRegion r;
bool isMaxSize;
int k;
};
class MainFrame :
@ -93,20 +94,25 @@ public:
void ShowFormInit();
void ShowHelpDocAsync();
void PrintRegionProc();
void PrintRegion();
bool IsMaxsized();
bool IsNormalsized();
bool IsMoving();
int GetDevicePixelRatio();
void SetFormRegion(int x, int y, int w, int h);
QRect GetFormRegion();
void saveNormalRegion();
protected:
void resizeEvent(QResizeEvent *event);
void closeEvent(QCloseEvent *event);
bool eventFilter(QObject *object, QEvent *event);
bool eventFilter(QObject *object, QEvent *event) override;
#ifdef _WIN32
void showEvent(QShowEvent *event);
#endif
void changeEvent(QEvent *event) override;
signals:
void sig_ParentNativeEvent(int msg);
@ -117,15 +123,14 @@ public slots:
void showNormal();
void showMaximized();
void showMinimized();
void moveToWinNaitiveNormal();
void showMinimized();
void OnParentNaitveWindowEvent(int msg);
void OnMoveWinShadow();
private:
void hide_border();
void show_border();
void writeSettings();
void saveNormalRegion();
void ReadSettings();
void AttachNativeWindow();
@ -139,10 +144,8 @@ private:
//IParentNativeEventCallback
void OnParentNativeEvent(ParentNativeEvent msg) override;
void SetFormRegion(int x, int y, int w, int h);
QRect GetFormRegion();
private:
toolbars::TitleBar *_titleBar;
MainWindow *_mainWindow;
@ -168,12 +171,11 @@ private:
#endif
bool _is_native_title;
bool _is_resize_ready;
bool _is_resize_ready;
WinNativeWidget *_parentNativeWidget;
FormInitInfo _initWndInfo;
FormRegion _normalRegion;
bool _is_max_status;
FormRegion _normalRegion;
QPoint _clickPos;
QRect _dragStartRegion;
QScreen *_move_start_screen;

View File

@ -84,8 +84,8 @@ class MainWindow :
Q_OBJECT
public:
static const int Min_Width = 800;
static const int Min_Height = 520;
static const int Min_Width = 350;
static const int Min_Height = 300;
static const int Base_Height = 150;
static const int Per_Chan_Height = 35;

View File

@ -30,13 +30,21 @@
#include <dwmapi.h>
#include <assert.h>
#include <QString>
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
#include <qmath.h>
#include "log.h"
#include "../config.h"
#define NAI_WIN_CREATE_STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN
#define FIXED_WIDTH(widget) (widget->minimumWidth() >= widget->maximumWidth())
#define FIXED_HEIGHT(widget) (widget->minimumHeight() >= widget->maximumHeight())
#define FIXED_SIZE(widget) (FIXED_WIDTH(widget) && FIXED_HEIGHT(widget))
namespace pv {
//-----------------------------WinNativeWidget
WinNativeWidget::WinNativeWidget(const int x, const int y, const int width, const int height)
{
_childWindow = nullptr;
@ -44,6 +52,8 @@ WinNativeWidget::WinNativeWidget(const int x, const int y, const int width, cons
_hWnd = NULL;
_event_callback = NULL;
_is_moving = false;
_shadow = NULL;
_cur_screen = NULL;
HBRUSH windowBackground = CreateSolidBrush(RGB(0, 0, 0));
HINSTANCE hInstance = GetModuleHandle(nullptr);
@ -55,7 +65,7 @@ WinNativeWidget::WinNativeWidget(const int x, const int y, const int width, cons
wcx.lpfnWndProc = WndProc;
wcx.cbClsExtra = 0;
wcx.cbWndExtra = 0;
wcx.lpszClassName = L"WindowClass";
wcx.lpszClassName = L"DSViewWindowClass";
wcx.hbrBackground = windowBackground;
wcx.hCursor = LoadCursor(hInstance, IDC_ARROW);
@ -66,15 +76,12 @@ WinNativeWidget::WinNativeWidget(const int x, const int y, const int width, cons
dsv_info("ERROR: can't register window class");
assert(false);
}
QString title = QApplication::applicationName() + " v" + QApplication::applicationVersion();
wchar_t title_string_buffer[50];
int title_len = title.toWCharArray(title_string_buffer);
title_string_buffer[title_len] = 0;
_hWnd = CreateWindow(L"WindowClass", title_string_buffer,
NAI_WIN_CREATE_STYLE, x, y,
width, height, 0, 0, hInstance, nullptr);
_hWnd = CreateWindow(L"DSViewWindowClass", L"DSView",
// WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CLIPCHILDREN,
WS_OVERLAPPEDWINDOW | WS_CLIPCHILDREN | WS_CLIPSIBLINGS,
x, y, width, height,
NULL, NULL, hInstance, NULL);
if (!_hWnd)
{
@ -89,6 +96,11 @@ WinNativeWidget::WinNativeWidget(const int x, const int y, const int width, cons
WinNativeWidget::~WinNativeWidget()
{
if (_hWnd){
if (_shadow){
_shadow->hide();
delete _shadow;
_shadow = NULL;
}
Show(false);
DestroyWindow(_hWnd);
}
@ -101,6 +113,8 @@ void WinNativeWidget::SetChildWidget(QWidget *w)
if (w != NULL){
_childWindow = (HWND)w->winId();
_shadow = new WinShadow(_hWnd, w);
_shadow->createWinId();
}
}
@ -115,6 +129,22 @@ LRESULT CALLBACK WinNativeWidget::WndProc(HWND hWnd, UINT message, WPARAM wParam
switch (message)
{
case WM_ACTIVATE:
{
switch (wParam)
{
case WA_ACTIVE:
case WA_CLICKACTIVE:
{
self->BeginShowShadow();
break;
}
case WA_INACTIVE:
{
break;
}
}
}
case WM_SYSCOMMAND:
{
if (wParam == SC_KEYMENU)
@ -122,12 +152,12 @@ LRESULT CALLBACK WinNativeWidget::WndProc(HWND hWnd, UINT message, WPARAM wParam
RECT winrect;
GetWindowRect(hWnd, &winrect);
TrackPopupMenu(GetSystemMenu(hWnd, false), TPM_TOPALIGN | TPM_LEFTALIGN, winrect.left + 5, winrect.top + 5, 0, hWnd, NULL);
break;
}
else
{
return DefWindowProc(hWnd, message, wParam, lParam);
}
break;;
}
case WM_NCCALCSIZE:
{
@ -150,24 +180,76 @@ LRESULT CALLBACK WinNativeWidget::WndProc(HWND hWnd, UINT message, WPARAM wParam
case WM_SIZE:
{
if (self->_childWindow != NULL){
self->ResizeChild();
self->ResizeChild();
if (self->_shadow){
if (self->IsNormalsized()){
self->_shadow->tryToShow();
}
else{
self->_shadow->hide();
}
}
self->MoveShadow();
}
break;
}
case WM_DPICHANGED:
if (self->_is_moving == false){
if (self->_event_callback != NULL){
self->_event_callback->OnParentNativeEvent(EV_SCREEN_DPI_CHANGED);
}
else{
self->UpdateChildDpi();
self->ResizeChild();
}
case WM_GETMINMAXINFO:
{
if (self->childWidget)
{
auto gw = self->childWidget;
int k = gw->windowHandle()->devicePixelRatio();
MINMAXINFO *mmi = reinterpret_cast<MINMAXINFO*>(lParam);
dsv_info("Dpi was changed.");
int border_width = 0;
QSize minimum = gw->minimumSize();
QSize sizeHint = gw->minimumSizeHint();
mmi->ptMinTrackSize.x = qFloor(qMax(minimum.width(), sizeHint.width()) *
k) + border_width * 2;
mmi->ptMinTrackSize.y = qFloor(qMax(minimum.height(), sizeHint.height()) *
k) + border_width;
QSize maximum = gw->maximumSize();
mmi->ptMaxTrackSize.x = qFloor(maximum.width() * k) + border_width * 2;
mmi->ptMaxTrackSize.y = qFloor(maximum.height() * k) + border_width;
}
break;
}
case WM_DISPLAYCHANGE:
{
dsv_info("Display changed.");
if (self->_event_callback != NULL){
self->_event_callback->OnParentNativeEvent(PARENT_EVENT_DISPLAY_CHANGED);
}
break;
}
case WM_WINDOWPOSCHANGING:
{
int st = 3;
if (self->IsMaxsized())
st = 1;
else if (self->IsNormalsized())
st = 2;
if (self->childWidget != NULL && self->_shadow){
if (st == 2){
if (self->childWidget->isVisible() && !self->_is_moving){
self->_shadow->showLater();
}
}
else {
self->_shadow->hide();
}
}
break;
}
}
return DefWindowProc(hWnd, message, wParam, lParam);
@ -177,27 +259,95 @@ void WinNativeWidget::ResizeChild()
{
if (_childWindow != NULL){
RECT rc;
GetClientRect(_hWnd, &rc);
GetWindowRect(_hWnd, &rc);
int w = rc.right;
int h = rc.bottom;
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(_hWnd, &wp);
if (wp.showCmd == SW_MAXIMIZE) {
int sp = 0;
int x = sp;
int y = sp;
int w = rc.right - rc.left - sp * 2;
int h = rc.bottom - rc.top - sp * 2;
if (IsMaxsized()) {
w -= 8;
h -= 8;
}
//childWidget->adjustSize();
MoveWindow(_childWindow, 0, 0, w - 1 , h - 1 , 1);
MoveWindow(_childWindow, 0, 0, w , h , 1);
childWidget->updateGeometry();
MoveWindow(_childWindow, x, y, w - 1 , h - 1 , 1);
MoveWindow(_childWindow, x, y, w , h , 1);
childWidget->updateGeometry();
}
}
void WinNativeWidget::ResizeSelf()
{
if (_hWnd)
{
RECT rc;
GetWindowRect(_hWnd, &rc);
int x = rc.left;
int y = rc.top;
int w = rc.right - rc.left;
int h = rc.bottom - rc.top;
static int times = 0;
times++;
if (times % 2 == 0){
w += 2;
h += 2;
}
else{
w -= 2;
h -= 2;
}
MoveWindow(_hWnd, x, y, w , h , 1);
}
}
void WinNativeWidget::MoveShadow()
{
if (!_shadow || !childWidget){
return;
}
if (IsNormalsized())
{
RECT rc;
GetWindowRect(_hWnd, &rc);
int bw = SHADOW_BORDER_WIDTH;
// bw = 20;
int x = rc.left;
int y = rc.top;
int w = rc.right - rc.left;
int h = rc.bottom - rc.top;
x -= bw;
y -= bw;
w += bw * 2;
h += bw * 2;
MoveWindow((HWND)_shadow->winId(), x, y, w , h , 1);
_shadow->setActive(isActiveWindow());
}
else{
_shadow->hide();
_shadow->setActive(false);
_shadow->update();
}
}
bool WinNativeWidget::isActiveWindow()
{
const HWND active_window = GetActiveWindow();
return ((active_window == _hWnd) || IsChild(_hWnd, active_window));
}
void WinNativeWidget::setGeometry(const int x, const int y, const int width, const int height)
{
// dsv_info("set parent window, x:%d, y:%d, w:%d, h:%d", x, y, width, height);
@ -210,6 +360,10 @@ void WinNativeWidget::Show(bool bShow)
{
if (_hWnd){
ShowWindow(_hWnd, bShow ? SW_SHOW : SW_HIDE);
if (!bShow && _shadow){
_shadow->hide();
}
}
}
@ -217,7 +371,20 @@ void WinNativeWidget::Show(bool bShow)
{
RECT rc;
GetClientRect(_hWnd, &rc);
MoveWindow(_hWnd, x, y, rc.right, rc.bottom, 0);
int w = rc.right;
int h = rc.bottom;
MoveWindow(_hWnd, x, y, w, h, 1);
if (_shadow != NULL){
auto scr = GetPointScreen();
if (_cur_screen && scr && scr != _cur_screen){
_shadow->windowHandle()->setScreen(scr);
}
_cur_screen = scr;
MoveShadow();
}
}
void WinNativeWidget::ShowNormal()
@ -246,6 +413,12 @@ void WinNativeWidget::UpdateChildDpi()
QScreen *scr = screenFromWindow(_hWnd);
if (scr != NULL && childWidget != NULL){
childWidget->windowHandle()->setScreen(scr);
if (_shadow){
_shadow->windowHandle()->setScreen(scr);
}
}
else{
dsv_info("ERROR: failed to update child's screen.");
}
}
@ -282,4 +455,48 @@ QScreen* WinNativeWidget::GetPointScreen()
return screenFromWindow(_hWnd);
}
void WinNativeWidget::OnDisplayChanged()
{
UpdateChildDpi();
ResizeChild();
RECT rc;
GetWindowRect(_hWnd, &rc);
int w = rc.right - rc.left;
int h = rc.bottom - rc.top;
MoveWindow(_hWnd, rc.left, rc.top, w + 1, h + 1, 1);
}
bool WinNativeWidget::IsMaxsized()
{
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(_hWnd, &wp);
return wp.showCmd == SW_MAXIMIZE;
}
bool WinNativeWidget::IsMinsized()
{
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(_hWnd, &wp);
return wp.showCmd == SW_MINIMIZE;
}
bool WinNativeWidget::IsNormalsized()
{
WINDOWPLACEMENT wp;
wp.length = sizeof(WINDOWPLACEMENT);
GetWindowPlacement(_hWnd, &wp);
return wp.showCmd == SW_SHOWNORMAL;
}
void WinNativeWidget::BeginShowShadow()
{
if (_shadow){
_shadow->show();
_shadow->showLater();
}
}
}

View File

@ -28,19 +28,23 @@
#include <Windows.h>
#include <Windowsx.h>
#include <QWidget>
#include <QByteArray>
#include "interface/icallbacks.h"
#include "winshadow.h"
#ifndef WM_DPICHANGED
#define WM_DPICHANGED 0x02E0
#endif
namespace pv {
class WinNativeWidget
{
public:
WinNativeWidget(const int x, const int y, const int width, const int height);
WinNativeWidget(const int x, const int y, const int width, const int heigh);
~WinNativeWidget();
static LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam);
@ -62,9 +66,11 @@ public:
void UpdateChildDpi();
void ResizeChild();
void ResizeSelf();
inline void SetMovingFlag(bool bMoving){
_is_moving = bMoving;
_cur_screen = NULL;
}
QScreen* GetPointScreen();
@ -73,15 +79,32 @@ public:
_event_callback = callback;
}
void OnDisplayChanged();
bool IsMaxsized();
bool IsMinsized();
bool IsNormalsized();
void MoveShadow();
inline WinShadow* Shadow(){
return _shadow;
}
bool isActiveWindow();
void BeginShowShadow();
private:
QScreen* screenFromWindow(HWND hwnd);
private:
QWidget* childWidget;
HWND _childWindow;
HWND _hWnd;
IParentNativeEventCallback *_event_callback;
bool _is_moving;
WinShadow *_shadow;
QScreen *_cur_screen;
};
}

426
DSView/pv/winshadow.cpp Normal file
View File

@ -0,0 +1,426 @@
/*
* This file is part of the DSView project.
* DSView is based on PulseView.
*
* Copyright (C) 2024 DreamSourceLab <support@dreamsourcelab.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "winshadow.h"
#include "log.h"
#include "config/appconfig.h"
#include "mainframe.h"
#include "mainwindow.h"
namespace pv {
namespace
{
const QColor dark_border0 = QColor(80, 80, 80, 255);
const QColor dark_border1 = QColor(48, 47, 47, 200);
const QColor dark_border2 = QColor(48, 47, 47, 150);
const QColor dark_border3 = QColor(48, 47, 47, 100);
const QColor dark_border4 = QColor(48, 47, 47, 10);
const QColor light_border0 = QColor(100, 100, 100, 255);
const QColor light_border1 = QColor(150, 150, 150, 150);
const QColor light_border2 = QColor(150, 150, 150, 100);
const QColor light_border3 = QColor(150, 150, 150, 50);
const QColor light_border4 = QColor(150, 150, 150, 0);
const int Margin = 4;
}
WinShadow::WinShadow(HWND hwnd, QWidget *parent) : QWidget(parent)
{
m_hwnd = HWND(hwnd);
m_active = true;
m_timer = NULL;
m_bClean = false;
_frame = NULL;
_hit_border = None;
_bDraging = false;
_freezing = false;
setWindowFlags(Qt::Window | Qt::FramelessWindowHint);
setAttribute(Qt::WA_NoSystemBackground);
setAttribute(Qt::WA_TranslucentBackground);
m_timer = new QTimer(this);
connect(m_timer, &QTimer::timeout, this, &WinShadow::show);
m_timer->setInterval(500);
m_timer->setSingleShot(true);
_layout = new QGridLayout(this);
_layout->setSpacing(0);
_layout->setContentsMargins(0,0,0,0);
setLayout(_layout);
if (true)
{
_top_left = new widgets::Border (TopLeft, this);
_top_left->setFixedSize(Margin, Margin);
_top_left->installEventFilter(this);
_top = new widgets::Border (Top, this);
_top->setFixedHeight(Margin);
_top->installEventFilter(this);
_top_right = new widgets::Border (TopRight, this);
_top_right->setFixedSize(Margin, Margin);
_top_right->installEventFilter(this);
_left = new widgets::Border (Left, this);
_left->setFixedWidth(Margin);
_left->installEventFilter(this);
_right = new widgets::Border (Right, this);
_right->setFixedWidth(Margin);
_right->installEventFilter(this);
_bottom_left = new widgets::Border (BottomLeft, this);
_bottom_left->setFixedSize(Margin, Margin);
_bottom_left->installEventFilter(this);
_bottom = new widgets::Border (Bottom, this);
_bottom->setFixedHeight(Margin);
_bottom->installEventFilter(this);
_bottom_right = new widgets::Border (BottomRight, this);
_bottom_right->setFixedSize(Margin, Margin);
_bottom_right->installEventFilter(this);
_layout->addWidget(_top_left, 0, 0);
_layout->addWidget(_top, 0, 1);
_layout->addWidget(_top_right, 0, 2);
_layout->addWidget(_left, 1, 0);
_layout->addWidget(_right, 1, 2);
_layout->addWidget(_bottom_left, 2, 0);
_layout->addWidget(_bottom, 2, 1);
_layout->addWidget(_bottom_right, 2, 2);
}
}
int WinShadow::shadowWidth()
{
return SHADOW_BORDER_WIDTH;
}
void WinShadow::showLater()
{
m_timer->stop();
m_timer->start();
}
void WinShadow::show()
{
m_bClean = false;
if (m_timer->isActive())
return;
if (!IsWindowEnabled(m_hwnd))
return;
if (GetForegroundWindow() != m_hwnd)
return;
Q_EMIT showSignal();
QWidget::show();
QWidget::raise();
SetWindowPos(m_hwnd, HWND_TOP, 0, 0, 0, 0, SWP_NOSIZE | SWP_NOMOVE | SWP_NOACTIVATE);
}
bool WinShadow::tryToShow()
{
if (!isVisible()){
show();
if (!m_timer->isActive()){
showLater();
}
return true;
}
return false;
}
void WinShadow::SetClean(bool bClean)
{
m_bClean = bClean;
if (isVisible()){
repaint();
}
}
void WinShadow::hide()
{
SetClean(true);
m_timer->stop();
if (!isVisible())
return;
QWidget::hide();
}
void WinShadow::setActive(bool active)
{
m_active = active;
repaint();
}
bool WinShadow::nativeEvent(const QByteArray &eventType, void *message, long *result)
{
MSG *msg = static_cast<MSG*>(message);
switch (msg->message)
{
case WM_ACTIVATE:
{
switch (msg->wParam)
{
case WA_ACTIVE:
case WA_CLICKACTIVE:
{
SetForegroundWindow(m_hwnd);
break;
}
default:
break;
}
break;
}
case WM_MOUSEACTIVATE:
{
SetForegroundWindow(m_hwnd);
*result = MA_NOACTIVATE;
return true;
}
/*
case WM_NCMOUSEMOVE:
case WM_NCLBUTTONDOWN:
case WM_NCLBUTTONUP:
case WM_NCLBUTTONDBLCLK:
case WM_NCHITTEST:
{
*result = long(SendMessageW(m_hwnd, msg->message, msg->wParam, msg->lParam));
return true;
}
*/
default:
break;
}
return QWidget::nativeEvent(eventType, message, result);
}
bool WinShadow::eventFilter(QObject *object, QEvent *event)
{
const QEvent::Type type = event->type();
const QMouseEvent *const mouse_event = (QMouseEvent*)event;
int newWidth = 0;
int newHeight = 0;
int newLeft = 0;
int newTop = 0;
if (_frame == NULL){
return QWidget::eventFilter(object, event);
}
if (type != QEvent::MouseMove
&& type != QEvent::MouseButtonPress
&& type != QEvent::MouseButtonRelease
&& type != QEvent::Leave){
return QWidget::eventFilter(object, event);
}
//when window is maximized, or is moving, call return
if (_frame->IsMaxsized() || _frame->IsMoving()){
return QWidget::eventFilter(object, event);
}
if (!_bDraging && type == QEvent::MouseMove && (!(mouse_event->buttons() | Qt::NoButton))){
if (object == _top_left) {
_hit_border = TopLeft;
setCursor(Qt::SizeFDiagCursor);
} else if (object == _bottom_right) {
_hit_border = BottomRight;
setCursor(Qt::SizeFDiagCursor);
} else if (object == _top_right) {
_hit_border = TopRight;
setCursor(Qt::SizeBDiagCursor);
} else if (object == _bottom_left) {
_hit_border = BottomLeft;
setCursor(Qt::SizeBDiagCursor);
} else if (object == _left) {
_hit_border = Left;
setCursor(Qt::SizeHorCursor);
} else if (object == _right) {
_hit_border = Right;
setCursor(Qt::SizeHorCursor);
} else if (object == _bottom) {
_hit_border = Bottom;
setCursor(Qt::SizeVerCursor);
} else if (object == _top) {
_hit_border = Top;
setCursor(Qt::SizeVerCursor);
} else {
_hit_border = None;
setCursor(Qt::ArrowCursor);
}
return QWidget::eventFilter(object, event);
}
if (type == QEvent::MouseMove) {
QPoint pt;
int k = _frame->GetDevicePixelRatio();
POINT p;
GetCursorPos(&p);
pt.setX(p.x);
pt.setY(p.y);
int datX = pt.x() - _clickPos.x();
int datY = pt.y() - _clickPos.y();
datX /= k;
datY /= k;
int l = _dragStartRegion.left();
int t = _dragStartRegion.top();
int r = _dragStartRegion.right();
int b = _dragStartRegion.bottom();
if(mouse_event->buttons().testFlag(Qt::LeftButton)) {
// Do nothing this time.
if (_freezing){
return QWidget::eventFilter(object, event);
}
int minW = MainWindow::Min_Width;
int minH = MainWindow::Min_Height;
switch (_hit_border) {
case TopLeft:
l += datX;
t += datY;
if (r - l < minW)
l = r - minW;
if (b - t < minH)
t = b - minH;
break;
case BottomLeft:
l += datX;
b += datY;
if (r - l < minW)
l = r - minW;
if (b - t < minH)
b = t + minH;
break;
case TopRight:
r += datX;
t += datY;
if (r - l < minW)
r = l + minW;
if (b - t < minH)
t = b - minH;
break;
case BottomRight:
r += datX;
b += datY;
if (r - l < minW)
r = l + minW;
if (b - t < minH)
b = t + minH;
break;
case Left:
l += datX;
if (r - l < minW)
l = r - minW;
break;
case Right:
r += datX;
if (r - l < minW)
r = l + minW;
break;
case Top:
t += datY;
if (b - t < minH)
t = b - minH;
break;
case Bottom:
b += datY;
if (b - t < minH)
b = t + minH;
break;
default:
r = l;
break;
}
if (r != l){
_frame->SetFormRegion(l, t, r-l, b-t);
_frame->saveNormalRegion();
}
return true;
}
}
else if (type == QEvent::MouseButtonPress) {
if (mouse_event->button() == Qt::LeftButton)
if (_hit_border != None)
_bDraging = true;
_timer.start(50);
POINT p;
GetCursorPos(&p);
_clickPos.setX(p.x);
_clickPos.setY(p.y);
_dragStartRegion = _frame->GetFormRegion();
}
else if (type == QEvent::MouseButtonRelease) {
if (mouse_event->button() == Qt::LeftButton) {
_bDraging = false;
_timer.stop();
}
}
else if (!_bDraging && type == QEvent::Leave) {
_hit_border = None;
setCursor(Qt::ArrowCursor);
}
return QWidget::eventFilter(object, event);
}
}

114
DSView/pv/winshadow.h Normal file
View File

@ -0,0 +1,114 @@
/*
* This file is part of the DSView project.
* DSView is based on PulseView.
*
* Copyright (C) 2024 DreamSourceLab <support@dreamsourcelab.com>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
*/
#ifndef WINSHADOW_H
#define WINSHADOW_H
#include <QtCore>
#include <QtGui>
#include <QtWidgets>
#include <windows.h>
#include <QGridLayout>
#include "widgets/border.h"
#define SHADOW_BORDER_WIDTH 4
namespace pv {
class MainFrame;
class WinShadow : public QWidget
{
Q_OBJECT
enum borderTypes{
None,
TopLeft,
Left,
BottomLeft,
Bottom,
BottomRight,
Right,
TopRight,
Top
}borderTypes;
public:
explicit WinShadow(HWND hwnd, QWidget *parent);
bool tryToShow();
void SetClean(bool bClean);
inline void SetFrame(MainFrame *frame){
_frame = frame;
}
Q_SIGNALS:
void showSignal();
public Q_SLOTS:
void showLater();
void show();
void hide();
void setActive(bool active);
int shadowWidth();
private:
//Functions
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
bool eventFilter(QObject *object, QEvent *event) override;
private:
QTimer *m_timer;
HWND m_hwnd;
bool m_active;
bool m_bClean;
QGridLayout *_layout;
widgets::Border *_left;
widgets::Border *_right;
widgets::Border *_top;
widgets::Border *_bottom;
widgets::Border *_top_left;
widgets::Border *_top_right;
widgets::Border *_bottom_left;
widgets::Border *_bottom_right;
MainFrame *_frame;
int _hit_border;
bool _bDraging;
QTimer _timer;
bool _freezing;
QPoint _clickPos;
QRect _dragStartRegion;
};
}
#endif // SHADOW_H