Merge pull request #1 from amnezia-vpn/ui/framelesswindow
UI/framelesswindow
This commit is contained in:
commit
13f9764853
8 changed files with 556 additions and 9 deletions
|
@ -66,13 +66,18 @@ win32 {
|
||||||
OTHER_FILES += platform_win/vpnclient.rc
|
OTHER_FILES += platform_win/vpnclient.rc
|
||||||
RC_FILE = platform_win/vpnclient.rc
|
RC_FILE = platform_win/vpnclient.rc
|
||||||
|
|
||||||
HEADERS +=
|
HEADERS += \
|
||||||
SOURCES +=
|
ui/framelesswindow.h \
|
||||||
|
|
||||||
|
SOURCES += \
|
||||||
|
ui/framelesswindow.cpp
|
||||||
|
|
||||||
VERSION = 1.0.0.0
|
VERSION = 1.0.0.0
|
||||||
QMAKE_TARGET_COMPANY = "AmneziaVPN"
|
QMAKE_TARGET_COMPANY = "AmneziaVPN"
|
||||||
QMAKE_TARGET_PRODUCT = "AmneziaVPN"
|
QMAKE_TARGET_PRODUCT = "AmneziaVPN"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
LIBS += \
|
LIBS += \
|
||||||
-luser32 \
|
-luser32 \
|
||||||
-lrasapi32 \
|
-lrasapi32 \
|
||||||
|
@ -87,4 +92,9 @@ win32 {
|
||||||
|
|
||||||
macx {
|
macx {
|
||||||
ICON = $$PWD/images/app.icns
|
ICON = $$PWD/images/app.icns
|
||||||
|
|
||||||
|
HEADERS += ui/macos_util.h
|
||||||
|
SOURCES += ui/macos_util.mm
|
||||||
|
|
||||||
|
LIBS += -framework Cocoa
|
||||||
}
|
}
|
||||||
|
|
306
client/ui/framelesswindow.cpp
Normal file
306
client/ui/framelesswindow.cpp
Normal file
|
@ -0,0 +1,306 @@
|
||||||
|
// This code is a part of Qt-Nice-Frameless-Window
|
||||||
|
// https://github.com/Bringer-of-Light/Qt-Nice-Frameless-Window
|
||||||
|
// Licensed by MIT License - https://github.com/Bringer-of-Light/Qt-Nice-Frameless-Window/blob/master/LICENSE
|
||||||
|
|
||||||
|
|
||||||
|
#include "framelesswindow.h"
|
||||||
|
#include <QApplication>
|
||||||
|
#include <QPoint>
|
||||||
|
#include <QSize>
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
#include <WinUser.h>
|
||||||
|
#include <windowsx.h>
|
||||||
|
#include <dwmapi.h>
|
||||||
|
#include <objidl.h> // Fixes error C2504: 'IUnknown' : base class undefined
|
||||||
|
#include <gdiplus.h>
|
||||||
|
#include <GdiPlusColor.h>
|
||||||
|
#pragma comment (lib,"Dwmapi.lib") // Adds missing library, fixes error LNK2019: unresolved external symbol __imp__DwmExtendFrameIntoClientArea
|
||||||
|
#pragma comment (lib,"user32.lib")
|
||||||
|
|
||||||
|
CFramelessWindow::CFramelessWindow(QWidget *parent)
|
||||||
|
: QMainWindow(parent),
|
||||||
|
m_titlebar(Q_NULLPTR),
|
||||||
|
m_borderWidth(5),
|
||||||
|
m_bJustMaximized(false),
|
||||||
|
m_bResizeable(true)
|
||||||
|
{
|
||||||
|
// setWindowFlag(Qt::Window,true);
|
||||||
|
// setWindowFlag(Qt::FramelessWindowHint, true);
|
||||||
|
// setWindowFlag(Qt::WindowSystemMenuHint, true);
|
||||||
|
// setWindowFlag() is not avaliable before Qt v5.9, so we should use setWindowFlags instead
|
||||||
|
|
||||||
|
setWindowFlags(windowFlags() | Qt::Window | Qt::FramelessWindowHint | Qt::WindowSystemMenuHint);
|
||||||
|
|
||||||
|
setResizeable(m_bResizeable);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFramelessWindow::setResizeable(bool resizeable)
|
||||||
|
{
|
||||||
|
bool visible = isVisible();
|
||||||
|
m_bResizeable = resizeable;
|
||||||
|
if (m_bResizeable){
|
||||||
|
setWindowFlags(windowFlags() | Qt::WindowMaximizeButtonHint);
|
||||||
|
// setWindowFlag(Qt::WindowMaximizeButtonHint);
|
||||||
|
|
||||||
|
//此行代码可以带回Aero效果,同时也带回了标题栏和边框,在nativeEvent()会再次去掉标题栏
|
||||||
|
//
|
||||||
|
//this line will get titlebar/thick frame/Aero back, which is exactly what we want
|
||||||
|
//we will get rid of titlebar and thick frame again in nativeEvent() later
|
||||||
|
HWND hwnd = (HWND)this->winId();
|
||||||
|
DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
|
||||||
|
::SetWindowLong(hwnd, GWL_STYLE, style | WS_MAXIMIZEBOX | WS_THICKFRAME | WS_CAPTION);
|
||||||
|
}else{
|
||||||
|
setWindowFlags(windowFlags() & ~Qt::WindowMaximizeButtonHint);
|
||||||
|
// setWindowFlag(Qt::WindowMaximizeButtonHint,false);
|
||||||
|
|
||||||
|
HWND hwnd = (HWND)this->winId();
|
||||||
|
DWORD style = ::GetWindowLong(hwnd, GWL_STYLE);
|
||||||
|
::SetWindowLong(hwnd, GWL_STYLE, style & ~WS_MAXIMIZEBOX & ~WS_CAPTION);
|
||||||
|
}
|
||||||
|
|
||||||
|
//保留一个像素的边框宽度,否则系统不会绘制边框阴影
|
||||||
|
//
|
||||||
|
//we better left 1 piexl width of border untouch, so OS can draw nice shadow around it
|
||||||
|
const MARGINS shadow = { 1, 1, 1, 1 };
|
||||||
|
DwmExtendFrameIntoClientArea(HWND(winId()), &shadow);
|
||||||
|
|
||||||
|
setVisible(visible);
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFramelessWindow::setResizeableAreaWidth(int width)
|
||||||
|
{
|
||||||
|
if (1 > width) width = 1;
|
||||||
|
m_borderWidth = width;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFramelessWindow::setTitleBar(QWidget* titlebar)
|
||||||
|
{
|
||||||
|
m_titlebar = titlebar;
|
||||||
|
if (!titlebar) return;
|
||||||
|
connect(titlebar, SIGNAL(destroyed(QObject*)), this, SLOT(onTitleBarDestroyed()));
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFramelessWindow::onTitleBarDestroyed()
|
||||||
|
{
|
||||||
|
if (m_titlebar == QObject::sender())
|
||||||
|
{
|
||||||
|
m_titlebar = Q_NULLPTR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFramelessWindow::addIgnoreWidget(QWidget* widget)
|
||||||
|
{
|
||||||
|
if (!widget) return;
|
||||||
|
if (m_whiteList.contains(widget)) return;
|
||||||
|
m_whiteList.append(widget);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool CFramelessWindow::nativeEvent(const QByteArray &eventType, void *message, long *result)
|
||||||
|
{
|
||||||
|
//Workaround for known bug -> check Qt forum : https://forum.qt.io/topic/93141/qtablewidget-itemselectionchanged/13
|
||||||
|
#if (QT_VERSION == QT_VERSION_CHECK(5, 11, 1))
|
||||||
|
MSG* msg = *reinterpret_cast<MSG**>(message);
|
||||||
|
#else
|
||||||
|
MSG* msg = reinterpret_cast<MSG*>(message);
|
||||||
|
#endif
|
||||||
|
|
||||||
|
switch (msg->message)
|
||||||
|
{
|
||||||
|
case WM_NCCALCSIZE:
|
||||||
|
{
|
||||||
|
NCCALCSIZE_PARAMS& params = *reinterpret_cast<NCCALCSIZE_PARAMS*>(msg->lParam);
|
||||||
|
if (params.rgrc[0].top != 0)
|
||||||
|
params.rgrc[0].top -= 1;
|
||||||
|
|
||||||
|
//this kills the window frame and title bar we added with WS_THICKFRAME and WS_CAPTION
|
||||||
|
*result = WVR_REDRAW;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
case WM_NCHITTEST:
|
||||||
|
{
|
||||||
|
*result = 0;
|
||||||
|
|
||||||
|
const LONG border_width = m_borderWidth;
|
||||||
|
RECT winrect;
|
||||||
|
GetWindowRect(HWND(winId()), &winrect);
|
||||||
|
|
||||||
|
long x = GET_X_LPARAM(msg->lParam);
|
||||||
|
long y = GET_Y_LPARAM(msg->lParam);
|
||||||
|
|
||||||
|
if(m_bResizeable)
|
||||||
|
{
|
||||||
|
|
||||||
|
bool resizeWidth = minimumWidth() != maximumWidth();
|
||||||
|
bool resizeHeight = minimumHeight() != maximumHeight();
|
||||||
|
|
||||||
|
if(resizeWidth)
|
||||||
|
{
|
||||||
|
//left border
|
||||||
|
if (x >= winrect.left && x < winrect.left + border_width)
|
||||||
|
{
|
||||||
|
*result = HTLEFT;
|
||||||
|
}
|
||||||
|
//right border
|
||||||
|
if (x < winrect.right && x >= winrect.right - border_width)
|
||||||
|
{
|
||||||
|
*result = HTRIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(resizeHeight)
|
||||||
|
{
|
||||||
|
//bottom border
|
||||||
|
if (y < winrect.bottom && y >= winrect.bottom - border_width)
|
||||||
|
{
|
||||||
|
*result = HTBOTTOM;
|
||||||
|
}
|
||||||
|
//top border
|
||||||
|
if (y >= winrect.top && y < winrect.top + border_width)
|
||||||
|
{
|
||||||
|
*result = HTTOP;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(resizeWidth && resizeHeight)
|
||||||
|
{
|
||||||
|
//bottom left corner
|
||||||
|
if (x >= winrect.left && x < winrect.left + border_width &&
|
||||||
|
y < winrect.bottom && y >= winrect.bottom - border_width)
|
||||||
|
{
|
||||||
|
*result = HTBOTTOMLEFT;
|
||||||
|
}
|
||||||
|
//bottom right corner
|
||||||
|
if (x < winrect.right && x >= winrect.right - border_width &&
|
||||||
|
y < winrect.bottom && y >= winrect.bottom - border_width)
|
||||||
|
{
|
||||||
|
*result = HTBOTTOMRIGHT;
|
||||||
|
}
|
||||||
|
//top left corner
|
||||||
|
if (x >= winrect.left && x < winrect.left + border_width &&
|
||||||
|
y >= winrect.top && y < winrect.top + border_width)
|
||||||
|
{
|
||||||
|
*result = HTTOPLEFT;
|
||||||
|
}
|
||||||
|
//top right corner
|
||||||
|
if (x < winrect.right && x >= winrect.right - border_width &&
|
||||||
|
y >= winrect.top && y < winrect.top + border_width)
|
||||||
|
{
|
||||||
|
*result = HTTOPRIGHT;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (0!=*result) return true;
|
||||||
|
|
||||||
|
//*result still equals 0, that means the cursor locate OUTSIDE the frame area
|
||||||
|
//but it may locate in titlebar area
|
||||||
|
if (!m_titlebar) return false;
|
||||||
|
|
||||||
|
//support highdpi
|
||||||
|
double dpr = this->devicePixelRatioF();
|
||||||
|
QPoint pos = m_titlebar->mapFromGlobal(QPoint(x/dpr,y/dpr));
|
||||||
|
|
||||||
|
if (!m_titlebar->rect().contains(pos)) return false;
|
||||||
|
QWidget* child = m_titlebar->childAt(pos);
|
||||||
|
if (!child)
|
||||||
|
{
|
||||||
|
*result = HTCAPTION;
|
||||||
|
return true;
|
||||||
|
}else{
|
||||||
|
if (m_whiteList.contains(child))
|
||||||
|
{
|
||||||
|
*result = HTCAPTION;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
} //end case WM_NCHITTEST
|
||||||
|
case WM_GETMINMAXINFO:
|
||||||
|
{
|
||||||
|
if (::IsZoomed(msg->hwnd)) {
|
||||||
|
RECT frame = { 0, 0, 0, 0 };
|
||||||
|
AdjustWindowRectEx(&frame, WS_OVERLAPPEDWINDOW, FALSE, 0);
|
||||||
|
|
||||||
|
//record frame area data
|
||||||
|
double dpr = this->devicePixelRatioF();
|
||||||
|
|
||||||
|
m_frames.setLeft(abs(frame.left)/dpr+0.5);
|
||||||
|
m_frames.setTop(abs(frame.bottom)/dpr+0.5);
|
||||||
|
m_frames.setRight(abs(frame.right)/dpr+0.5);
|
||||||
|
m_frames.setBottom(abs(frame.bottom)/dpr+0.5);
|
||||||
|
|
||||||
|
QMainWindow::setContentsMargins(m_frames.left()+m_margins.left(), \
|
||||||
|
m_frames.top()+m_margins.top(), \
|
||||||
|
m_frames.right()+m_margins.right(), \
|
||||||
|
m_frames.bottom()+m_margins.bottom());
|
||||||
|
m_bJustMaximized = true;
|
||||||
|
}else {
|
||||||
|
if (m_bJustMaximized)
|
||||||
|
{
|
||||||
|
QMainWindow::setContentsMargins(m_margins);
|
||||||
|
m_frames = QMargins();
|
||||||
|
m_bJustMaximized = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
return QMainWindow::nativeEvent(eventType, message, result);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void CFramelessWindow::setContentsMargins(const QMargins &margins)
|
||||||
|
{
|
||||||
|
QMainWindow::setContentsMargins(margins+m_frames);
|
||||||
|
m_margins = margins;
|
||||||
|
}
|
||||||
|
void CFramelessWindow::setContentsMargins(int left, int top, int right, int bottom)
|
||||||
|
{
|
||||||
|
QMainWindow::setContentsMargins(left+m_frames.left(),\
|
||||||
|
top+m_frames.top(), \
|
||||||
|
right+m_frames.right(), \
|
||||||
|
bottom+m_frames.bottom());
|
||||||
|
m_margins.setLeft(left);
|
||||||
|
m_margins.setTop(top);
|
||||||
|
m_margins.setRight(right);
|
||||||
|
m_margins.setBottom(bottom);
|
||||||
|
}
|
||||||
|
QMargins CFramelessWindow::contentsMargins() const
|
||||||
|
{
|
||||||
|
QMargins margins = QMainWindow::contentsMargins();
|
||||||
|
margins -= m_frames;
|
||||||
|
return margins;
|
||||||
|
}
|
||||||
|
void CFramelessWindow::getContentsMargins(int *left, int *top, int *right, int *bottom) const
|
||||||
|
{
|
||||||
|
QMainWindow::getContentsMargins(left,top,right,bottom);
|
||||||
|
if (!(left&&top&&right&&bottom)) return;
|
||||||
|
if (isMaximized())
|
||||||
|
{
|
||||||
|
*left -= m_frames.left();
|
||||||
|
*top -= m_frames.top();
|
||||||
|
*right -= m_frames.right();
|
||||||
|
*bottom -= m_frames.bottom();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
QRect CFramelessWindow::contentsRect() const
|
||||||
|
{
|
||||||
|
QRect rect = QMainWindow::contentsRect();
|
||||||
|
int width = rect.width();
|
||||||
|
int height = rect.height();
|
||||||
|
rect.setLeft(rect.left() - m_frames.left());
|
||||||
|
rect.setTop(rect.top() - m_frames.top());
|
||||||
|
rect.setWidth(width);
|
||||||
|
rect.setHeight(height);
|
||||||
|
return rect;
|
||||||
|
}
|
||||||
|
void CFramelessWindow::showFullScreen()
|
||||||
|
{
|
||||||
|
if (isMaximized())
|
||||||
|
{
|
||||||
|
QMainWindow::setContentsMargins(m_margins);
|
||||||
|
m_frames = QMargins();
|
||||||
|
}
|
||||||
|
QMainWindow::showFullScreen();
|
||||||
|
}
|
||||||
|
|
||||||
|
#endif //Q_OS_WIN
|
158
client/ui/framelesswindow.h
Normal file
158
client/ui/framelesswindow.h
Normal file
|
@ -0,0 +1,158 @@
|
||||||
|
// This code is a part of Qt-Nice-Frameless-Window
|
||||||
|
// https://github.com/Bringer-of-Light/Qt-Nice-Frameless-Window
|
||||||
|
// Licensed by MIT License - https://github.com/Bringer-of-Light/Qt-Nice-Frameless-Window/blob/master/LICENSE
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef CFRAMELESSWINDOW_H
|
||||||
|
#define CFRAMELESSWINDOW_H
|
||||||
|
#include "qsystemdetection.h"
|
||||||
|
#include <QObject>
|
||||||
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
//A nice frameless window for both Windows and OS X
|
||||||
|
//Author: Bringer-of-Light
|
||||||
|
//Github: https://github.com/Bringer-of-Light/Qt-Nice-Frameless-Window
|
||||||
|
// Usage: use "CFramelessWindow" as base class instead of "QMainWindow", and enjoy
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
#include <QWidget>
|
||||||
|
#include <QList>
|
||||||
|
#include <QMargins>
|
||||||
|
#include <QRect>
|
||||||
|
class CFramelessWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit CFramelessWindow(QWidget *parent = 0);
|
||||||
|
public:
|
||||||
|
|
||||||
|
//设置是否可以通过鼠标调整窗口大小
|
||||||
|
//if resizeable is set to false, then the window can not be resized by mouse
|
||||||
|
//but still can be resized programtically
|
||||||
|
void setResizeable(bool resizeable=true);
|
||||||
|
bool isResizeable(){return m_bResizeable;}
|
||||||
|
|
||||||
|
//设置可调整大小区域的宽度,在此区域内,可以使用鼠标调整窗口大小
|
||||||
|
//set border width, inside this aera, window can be resized by mouse
|
||||||
|
void setResizeableAreaWidth(int width = 5);
|
||||||
|
protected:
|
||||||
|
//设置一个标题栏widget,此widget会被当做标题栏对待
|
||||||
|
//set a widget which will be treat as SYSTEM titlebar
|
||||||
|
void setTitleBar(QWidget* titlebar);
|
||||||
|
|
||||||
|
//在标题栏控件内,也可以有子控件如标签控件“label1”,此label1遮盖了标题栏,导致不能通过label1拖动窗口
|
||||||
|
//要解决此问题,使用addIgnoreWidget(label1)
|
||||||
|
//generally, we can add widget say "label1" on titlebar, and it will cover the titlebar under it
|
||||||
|
//as a result, we can not drag and move the MainWindow with this "label1" again
|
||||||
|
//we can fix this by add "label1" to a ignorelist, just call addIgnoreWidget(label1)
|
||||||
|
void addIgnoreWidget(QWidget* widget);
|
||||||
|
|
||||||
|
bool nativeEvent(const QByteArray &eventType, void *message, long *result);
|
||||||
|
private slots:
|
||||||
|
void onTitleBarDestroyed();
|
||||||
|
public:
|
||||||
|
void setContentsMargins(const QMargins &margins);
|
||||||
|
void setContentsMargins(int left, int top, int right, int bottom);
|
||||||
|
QMargins contentsMargins() const;
|
||||||
|
QRect contentsRect() const;
|
||||||
|
void getContentsMargins(int *left, int *top, int *right, int *bottom) const;
|
||||||
|
public slots:
|
||||||
|
void showFullScreen();
|
||||||
|
private:
|
||||||
|
QWidget* m_titlebar;
|
||||||
|
QList<QWidget*> m_whiteList;
|
||||||
|
int m_borderWidth;
|
||||||
|
|
||||||
|
QMargins m_margins;
|
||||||
|
QMargins m_frames;
|
||||||
|
bool m_bJustMaximized;
|
||||||
|
|
||||||
|
bool m_bResizeable;
|
||||||
|
};
|
||||||
|
|
||||||
|
#elif defined Q_OS_MAC
|
||||||
|
#include <QMouseEvent>
|
||||||
|
#include <QResizeEvent>
|
||||||
|
#include <QPoint>
|
||||||
|
class CFramelessWindow : public QMainWindow
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit CFramelessWindow(QWidget *parent = 0);
|
||||||
|
private:
|
||||||
|
void initUI();
|
||||||
|
public:
|
||||||
|
//设置可拖动区域的高度,在此区域内,可以通过鼠标拖动窗口, 0表示整个窗口都可拖动
|
||||||
|
//In draggable area, window can be moved by mouse, (height = 0) means that the whole window is draggable
|
||||||
|
void setDraggableAreaHeight(int height = 0);
|
||||||
|
|
||||||
|
//只有OS X10.10及以后系统,才支持OS X原生样式包括:三个系统按钮、窗口圆角、窗口阴影
|
||||||
|
//类初始化完成后,可以通过此函数查看是否已经启用了原生样式。如果未启动,需要自定义关闭按钮、最小化按钮、最大化按钮
|
||||||
|
//Native style(three system button/ round corner/ drop shadow) works only on OS X 10.10 or later
|
||||||
|
//after init, we should check whether NativeStyle is OK with this function
|
||||||
|
//if NOT ok, we should implement close button/ min button/ max button ourself
|
||||||
|
bool isNativeStyleOK() {return m_bNativeSystemBtn;}
|
||||||
|
|
||||||
|
//如果设置setCloseBtnQuit(false),那么点击关闭按钮后,程序不会退出,而是会隐藏,只有在OS X 10.10 及以后系统中有效
|
||||||
|
//if setCloseBtnQuit(false), then when close button is clicked, the application will hide itself instead of quit
|
||||||
|
//be carefull, after you set this to false, you can NOT change it to true again
|
||||||
|
//this function should be called inside of the constructor function of derived classes, and can NOT be called more than once
|
||||||
|
//only works for OS X 10.10 or later
|
||||||
|
void setCloseBtnQuit(bool bQuit = true);
|
||||||
|
|
||||||
|
//启用或禁用关闭按钮,只有在isNativeStyleOK()返回true的情况下才有效
|
||||||
|
//enable or disable Close button, only worked if isNativeStyleOK() returns true
|
||||||
|
void setCloseBtnEnabled(bool bEnable = true);
|
||||||
|
|
||||||
|
//启用或禁用最小化按钮,只有在isNativeStyleOK()返回true的情况下才有效
|
||||||
|
//enable or disable Miniaturize button, only worked if isNativeStyleOK() returns true
|
||||||
|
void setMinBtnEnabled(bool bEnable = true);
|
||||||
|
|
||||||
|
//启用或禁用zoom(最大化)按钮,只有在isNativeStyleOK()返回true的情况下才有效
|
||||||
|
//enable or disable Zoom button(fullscreen button), only worked if isNativeStyleOK() returns true
|
||||||
|
void setZoomBtnEnabled(bool bEnable = true);
|
||||||
|
|
||||||
|
bool isCloseBtnEnabled() {return m_bIsCloseBtnEnabled;}
|
||||||
|
bool isMinBtnEnabled() {return m_bIsMinBtnEnabled;}
|
||||||
|
bool isZoomBtnEnabled() {return m_bIsZoomBtnEnabled;}
|
||||||
|
protected:
|
||||||
|
void mousePressEvent(QMouseEvent *event);
|
||||||
|
void mouseReleaseEvent(QMouseEvent *event);
|
||||||
|
void mouseMoveEvent(QMouseEvent *event);
|
||||||
|
private:
|
||||||
|
int m_draggableHeight;
|
||||||
|
bool m_bWinMoving;
|
||||||
|
bool m_bMousePressed;
|
||||||
|
QPoint m_MousePos;
|
||||||
|
QPoint m_WindowPos;
|
||||||
|
bool m_bCloseBtnQuit;
|
||||||
|
bool m_bNativeSystemBtn;
|
||||||
|
bool m_bIsCloseBtnEnabled, m_bIsMinBtnEnabled, m_bIsZoomBtnEnabled;
|
||||||
|
|
||||||
|
//===============================================
|
||||||
|
//TODO
|
||||||
|
//下面的代码是试验性质的
|
||||||
|
//tentative code
|
||||||
|
|
||||||
|
//窗口从全屏状态恢复正常大小时,标题栏又会出现,原因未知。
|
||||||
|
//默认情况下,系统的最大化按钮(zoom button)是进入全屏,为了避免标题栏重新出现的问题,
|
||||||
|
//以上代码已经重新定义了系统zoom button的行为,是其功能变为最大化而不是全屏
|
||||||
|
//以下代码尝试,每次窗口从全屏状态恢复正常大小时,都再次进行设置,以消除标题栏
|
||||||
|
//after the window restore from fullscreen mode, the titlebar will show again, it looks like a BUG
|
||||||
|
//on OS X 10.10 and later, click the system green button (zoom button) will make the app become fullscreen
|
||||||
|
//so we have override it's action to "maximized" in the CFramelessWindow Constructor function
|
||||||
|
//but we may try something else such as delete the titlebar again and again...
|
||||||
|
private:
|
||||||
|
bool m_bTitleBarVisible;
|
||||||
|
|
||||||
|
void setTitlebarVisible(bool bTitlebarVisible = false);
|
||||||
|
bool isTitlebarVisible() {return m_bTitleBarVisible;}
|
||||||
|
private slots:
|
||||||
|
void onRestoreFromFullScreen();
|
||||||
|
signals:
|
||||||
|
void restoreFromFullScreen();
|
||||||
|
protected:
|
||||||
|
void resizeEvent(QResizeEvent *event);
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif // CFRAMELESSWINDOW_H
|
8
client/ui/macos_util.h
Normal file
8
client/ui/macos_util.h
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
#ifndef OSXUTIL_H
|
||||||
|
#define OSXUTIL_H
|
||||||
|
#include <QDialog>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
void fixWidget(QWidget *widget);
|
||||||
|
|
||||||
|
#endif
|
48
client/ui/macos_util.mm
Normal file
48
client/ui/macos_util.mm
Normal file
|
@ -0,0 +1,48 @@
|
||||||
|
#include <QMainWindow>
|
||||||
|
#include <Cocoa/Cocoa.h>
|
||||||
|
#include "macos_util.h"
|
||||||
|
|
||||||
|
//this Objective-c class is used to override the action of system close button and zoom button
|
||||||
|
//https://stackoverflow.com/questions/27643659/setting-c-function-as-selector-for-nsbutton-produces-no-results
|
||||||
|
@interface ButtonPasser : NSObject{
|
||||||
|
}
|
||||||
|
@property(readwrite) QMainWindow* window;
|
||||||
|
+ (void)closeButtonAction:(id)sender;
|
||||||
|
- (void)zoomButtonAction:(id)sender;
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation ButtonPasser{
|
||||||
|
}
|
||||||
|
+ (void)closeButtonAction:(id)sender
|
||||||
|
{
|
||||||
|
Q_UNUSED(sender);
|
||||||
|
ProcessSerialNumber pn;
|
||||||
|
GetFrontProcess (&pn);
|
||||||
|
ShowHideProcess(&pn,false);
|
||||||
|
}
|
||||||
|
- (void)zoomButtonAction:(id)sender
|
||||||
|
{
|
||||||
|
Q_UNUSED(sender);
|
||||||
|
if (0 == self.window) return;
|
||||||
|
if (self.window->isMaximized()) self.window->showNormal();
|
||||||
|
else self.window->showMaximized();
|
||||||
|
}
|
||||||
|
@end
|
||||||
|
|
||||||
|
void fixWidget(QWidget *widget)
|
||||||
|
{
|
||||||
|
NSView *view = (NSView *)widget->winId();
|
||||||
|
if (0 == view) return;
|
||||||
|
NSWindow *window = view.window;
|
||||||
|
if (0 == window) return;
|
||||||
|
|
||||||
|
//override the action of close button
|
||||||
|
//https://stackoverflow.com/questions/27643659/setting-c-function-as-selector-for-nsbutton-produces-no-results
|
||||||
|
//https://developer.apple.com/library/content/documentation/General/Conceptual/CocoaEncyclopedia/Target-Action/Target-Action.html
|
||||||
|
// NSButton *closeButton = [window standardWindowButton:NSWindowCloseButton];
|
||||||
|
// [closeButton setTarget:[ButtonPasser class]];
|
||||||
|
// [closeButton setAction:@selector(closeButtonAction:)];
|
||||||
|
|
||||||
|
[[window standardWindowButton:NSWindowZoomButton] setHidden:YES];
|
||||||
|
[[window standardWindowButton:NSWindowMiniaturizeButton] setHidden:YES];
|
||||||
|
}
|
|
@ -14,8 +14,16 @@
|
||||||
#include "utils.h"
|
#include "utils.h"
|
||||||
#include "vpnconnection.h"
|
#include "vpnconnection.h"
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
#include "ui/macos_util.h"
|
||||||
|
#endif
|
||||||
|
|
||||||
MainWindow::MainWindow(QWidget *parent) :
|
MainWindow::MainWindow(QWidget *parent) :
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
CFramelessWindow(parent),
|
||||||
|
#else
|
||||||
QMainWindow(parent),
|
QMainWindow(parent),
|
||||||
|
#endif
|
||||||
ui(new Ui::MainWindow),
|
ui(new Ui::MainWindow),
|
||||||
m_settings(new Settings),
|
m_settings(new Settings),
|
||||||
m_vpnConnection(nullptr)
|
m_vpnConnection(nullptr)
|
||||||
|
@ -23,12 +31,15 @@ MainWindow::MainWindow(QWidget *parent) :
|
||||||
ui->setupUi(this);
|
ui->setupUi(this);
|
||||||
ui->widget_tittlebar->installEventFilter(this);
|
ui->widget_tittlebar->installEventFilter(this);
|
||||||
|
|
||||||
setWindowFlags(Qt:: ToolTip | Qt::CustomizeWindowHint | Qt::FramelessWindowHint | Qt::WindowStaysOnTopHint);
|
|
||||||
setAttribute(Qt::WA_TranslucentBackground);
|
|
||||||
|
|
||||||
ui->stackedWidget_main->setSpeed(200);
|
ui->stackedWidget_main->setSpeed(200);
|
||||||
ui->stackedWidget_main->setAnimation(QEasingCurve::Linear);
|
ui->stackedWidget_main->setAnimation(QEasingCurve::Linear);
|
||||||
|
|
||||||
|
#ifdef Q_OS_MAC
|
||||||
|
ui->widget_tittlebar->hide();
|
||||||
|
ui->stackedWidget_main->move(0,0);
|
||||||
|
fixWidget(this);
|
||||||
|
#endif
|
||||||
|
|
||||||
// Post initialization
|
// Post initialization
|
||||||
|
|
||||||
if (m_settings->haveAuthData()) {
|
if (m_settings->haveAuthData()) {
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
|
|
||||||
#include <QMainWindow>
|
#include <QMainWindow>
|
||||||
|
|
||||||
|
#include "framelesswindow.h"
|
||||||
#include "vpnprotocol.h"
|
#include "vpnprotocol.h"
|
||||||
|
|
||||||
class Settings;
|
class Settings;
|
||||||
|
@ -15,7 +16,12 @@ class MainWindow;
|
||||||
/**
|
/**
|
||||||
* @brief The MainWindow class - Main application window
|
* @brief The MainWindow class - Main application window
|
||||||
*/
|
*/
|
||||||
|
#ifdef Q_OS_WIN
|
||||||
|
class MainWindow : public CFramelessWindow
|
||||||
|
#else
|
||||||
class MainWindow : public QMainWindow
|
class MainWindow : public QMainWindow
|
||||||
|
#endif
|
||||||
|
|
||||||
{
|
{
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
|
||||||
|
|
|
@ -6,8 +6,8 @@
|
||||||
<rect>
|
<rect>
|
||||||
<x>0</x>
|
<x>0</x>
|
||||||
<y>0</y>
|
<y>0</y>
|
||||||
<width>390</width>
|
<width>380</width>
|
||||||
<height>685</height>
|
<height>670</height>
|
||||||
</rect>
|
</rect>
|
||||||
</property>
|
</property>
|
||||||
<property name="windowTitle">
|
<property name="windowTitle">
|
||||||
|
@ -169,8 +169,8 @@ QScrollBar::add-page:vertical, QScrollBar::sub-page:vertical {
|
||||||
<widget class="QWidget" name="widget_main" native="true">
|
<widget class="QWidget" name="widget_main" native="true">
|
||||||
<property name="geometry">
|
<property name="geometry">
|
||||||
<rect>
|
<rect>
|
||||||
<x>5</x>
|
<x>0</x>
|
||||||
<y>5</y>
|
<y>0</y>
|
||||||
<width>380</width>
|
<width>380</width>
|
||||||
<height>670</height>
|
<height>670</height>
|
||||||
</rect>
|
</rect>
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue