copy WindowButtonsProxy from electron

This commit is contained in:
Altair Wei 2021-10-10 21:08:42 +08:00
parent c8cfefb3ca
commit 92681dc9d9
8 changed files with 342 additions and 28 deletions

View File

@ -107,7 +107,6 @@ void MainWindow::showEvent(QShowEvent *event)
titleBarWidget->maximizeButton->hide();
titleBarWidget->closeButton->hide();
Utilities::showMacWindowButton(windowHandle());
Utilities::setStandardWindowButtonsOffset(win, QPoint(0, -5));
#endif // Q_OS_MAC
inited = true;
}

View File

@ -37,7 +37,6 @@ void FLWindow::initFramelessWindow()
m_maximizeButton->hide();
m_closeButton->hide();
Utilities::showMacWindowButton(windowHandle());
Utilities::setStandardWindowButtonsOffset(windowHandle(), QPoint(10, 10));
#endif
}
@ -65,18 +64,6 @@ bool FLWindow::nativeEvent(const QByteArray &eventType, void *message, long *res
}
#endif // Q_OS_WIN
#ifdef Q_OS_MAC
void FLWindow::resizeEvent(QResizeEvent *event)
{
auto win = windowHandle();
if (win) {
Utilities::setStandardWindowButtonsOffset(win, QPoint(10, 10));
}
QWidget::resizeEvent(event);
}
#endif // Q_OS_MAC
void FLWindow::setupUi()
{
resize(800, 600);

View File

@ -18,10 +18,6 @@ protected:
bool nativeEvent(const QByteArray &eventType, void *message, long *result) override;
#endif // Q_OS_WIN
#ifdef Q_OS_MAC
void resizeEvent(QResizeEvent *event) override;
#endif // Q_OS_MAC
private:
void initFramelessWindow();
void setupUi();

View File

@ -24,7 +24,11 @@ if(WIN32)
)
else()
if(APPLE)
list(APPEND SOURCES core/utilities_macos.mm)
list(APPEND SOURCES
core/utilities_macos.mm
core/window_buttons_proxy.h
core/window_buttons_proxy.mm
)
else()
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS X11Extras REQUIRED)
list(APPEND SOURCES core/utilities_linux.cpp)

View File

@ -81,6 +81,7 @@ FRAMELESSHELPER_API bool isMacMainWindow(QWindow *w);
FRAMELESSHELPER_API bool makeMacKeyWindow(QWindow *w);
FRAMELESSHELPER_API bool makeMacMainWindow(QWindow *w);
FRAMELESSHELPER_API bool setStandardWindowButtonsOffset(QWindow *w, const QPoint &offset);
FRAMELESSHELPER_API bool setTrafficLightPosition(QWindow *w, const QPoint &pos);
#endif // Q_OS_MAC
}

View File

@ -32,6 +32,8 @@
#include <QtCore/qlist.h>
#include <QtCore/qdebug.h>
#include "window_buttons_proxy.h"
FRAMELESSHELPER_BEGIN_NAMESPACE
namespace Utilities {
@ -241,8 +243,8 @@ static void overrideNSWindowMethods(NSWindow* window)
cls, @selector(canBecomeMainWindow), (IMP) __canBecomeMainWindow);
gOrigSendEvent = (sendEventType) replaceMethod(
cls, @selector(sendEvent:), (IMP) __sendEvent);
gOrigIsFlipped = (isFlippedType) replaceMethod(
cls, @selector (isFlipped), (IMP) __isFlipped);
//gOrigIsFlipped = (isFlippedType) replaceMethod(
// cls, @selector (isFlipped), (IMP) __isFlipped);
gNSWindowOverrode = true;
}
@ -271,15 +273,51 @@ static void restoreNSWindowMethods(NSWindow* window)
restoreMethod(cls, @selector(sendEvent:), (IMP) gOrigSendEvent);
gOrigSendEvent = nullptr;
restoreMethod(cls, @selector(isFlipped), (IMP) gOrigIsFlipped);
gOrigIsFlipped = nullptr;
//restoreMethod(cls, @selector(isFlipped), (IMP) gOrigIsFlipped);
//gOrigIsFlipped = nullptr;
gNSWindowOverrode = false;
}
}
static QHash<QWindow*, NSWindow*> gQWindowToNSWindow;
class NSWindowData
{
private:
NSWindow* m_window;
WindowButtonsProxy *m_buttonProxy;
bool m_windowButtonVisibility;
QPoint m_trafficLightPosition;
public:
NSWindowData(NSWindow *window)
: m_windowButtonVisibility(false)
, m_buttonProxy(nullptr)
, m_window(window)
{
overrideNSWindowMethods(window);
m_buttonProxy = [[WindowButtonsProxy alloc] initWithWindow:window];
}
~NSWindowData()
{
restoreNSWindowMethods(m_window);
[m_buttonProxy release];
}
NSWindow* window() { return m_window; }
QPoint trafficLightPosition() { return m_trafficLightPosition; }
bool setTrafficLightPosition(const QPoint &pos) {
m_trafficLightPosition = pos;
if (m_buttonProxy) {
[m_buttonProxy setMargin:m_trafficLightPosition];
}
return true;
}
};
static QHash<QWindow*, NSWindowData*> gQWindowToNSWindow;
static NSWindow* getNSWindow(QWindow* w)
{
@ -303,8 +341,8 @@ bool setMacWindowHook(QWindow* w)
if (nswindow == nullptr)
return false;
gQWindowToNSWindow.insert(w, nswindow);
overrideNSWindowMethods(nswindow);
NSWindowData *obj = new NSWindowData(nswindow);
gQWindowToNSWindow.insert(w, obj);
return true;
}
@ -313,9 +351,10 @@ bool unsetMacWindowHook(QWindow* w)
{
if (!gQWindowToNSWindow.contains(w))
return false;
NSWindow* obj = gQWindowToNSWindow[w];
NSWindowData* obj = gQWindowToNSWindow[w];
gQWindowToNSWindow.remove(w);
restoreNSWindowMethods(obj);
delete obj;
return true;
}
@ -487,6 +526,12 @@ bool setStandardWindowButtonsOffset(QWindow *w, const QPoint &offset)
return true;
}
bool setTrafficLightPosition(QWindow *w, const QPoint &pos)
{
NSWindowData* obj = gQWindowToNSWindow[w];
return obj->setTrafficLightPosition(pos);
}
} // namespace Utilities
FRAMELESSHELPER_END_NAMESPACE

View File

@ -0,0 +1,57 @@
// Copyright (c) 2021 Microsoft, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#ifndef SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_PROXY_H_
#define SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_PROXY_H_
#include <memory>
#import <Cocoa/Cocoa.h>
#include <QtCore/qpoint.h>
@class WindowButtonsProxy;
// A helper view that floats above the window buttons.
@interface ButtonsAreaHoverView : NSView {
@private
WindowButtonsProxy* proxy_;
}
- (id)initWithProxy:(WindowButtonsProxy*)proxy;
@end
// Manipulating the window buttons.
@interface WindowButtonsProxy : NSObject {
@private
NSWindow* window_;
// Current left-top margin of buttons.
QPoint margin_;
// The default left-top margin.
QPoint default_margin_;
// Track mouse moves above window buttons.
BOOL show_on_hover_;
BOOL mouse_inside_;
NSTrackingArea* tracking_area_;
ButtonsAreaHoverView* hover_view_;
}
- (id)initWithWindow:(NSWindow*)window;
- (void)setVisible:(BOOL)visible;
- (BOOL)isVisible;
// Only show window buttons when mouse moves above them.
- (void)setShowOnHover:(BOOL)yes;
// Set left-top margin of the window buttons..
- (void)setMargin:(const QPoint&)margin;
// Return the bounds of all 3 buttons, with margin on all sides.
- (NSRect)getButtonsContainerBounds;
- (void)redraw;
- (void)updateTrackingAreas;
@end
#endif // SHELL_BROWSER_UI_COCOA_WINDOW_BUTTONS_PROXY_H_

View File

@ -0,0 +1,225 @@
// Copyright (c) 2021 Microsoft, Inc.
// Use of this source code is governed by the MIT license that can be
// found in the LICENSE file.
#include "window_buttons_proxy.h"
@implementation ButtonsAreaHoverView : NSView
- (id)initWithProxy:(WindowButtonsProxy*)proxy {
if ((self = [super init])) {
proxy_ = proxy;
}
return self;
}
// Ignore all mouse events.
- (NSView*)hitTest:(NSPoint)aPoint {
return nil;
}
- (void)updateTrackingAreas {
[proxy_ updateTrackingAreas];
}
@end
@implementation WindowButtonsProxy
- (id)initWithWindow:(NSWindow*)window {
window_ = window;
show_on_hover_ = NO;
mouse_inside_ = NO;
// Remember the default margin.
margin_ = default_margin_ = [self getCurrentMargin];
return self;
}
- (void)dealloc {
if (hover_view_)
[hover_view_ removeFromSuperview];
[super dealloc];
}
- (void)setVisible:(BOOL)visible {
NSView* titleBarContainer = [self titleBarContainer];
if (!titleBarContainer)
return;
[titleBarContainer setHidden:!visible];
}
- (BOOL)isVisible {
NSView* titleBarContainer = [self titleBarContainer];
if (!titleBarContainer)
return YES;
return ![titleBarContainer isHidden];
}
- (void)setShowOnHover:(BOOL)yes {
NSView* titleBarContainer = [self titleBarContainer];
if (!titleBarContainer)
return;
show_on_hover_ = yes;
// Put a transparent view above the window buttons so we can track mouse
// events when mouse enter/leave the window buttons.
if (show_on_hover_) {
auto old_ptr = hover_view_;
hover_view_ = [[ButtonsAreaHoverView alloc] initWithProxy:self];
[old_ptr release];
[hover_view_ setFrame:[self getButtonsBounds]];
[titleBarContainer addSubview:hover_view_];
} else {
[hover_view_ removeFromSuperview];
[hover_view_ release];
hover_view_ = nullptr;
}
[self updateButtonsVisibility];
}
- (void)setMargin:(const QPoint&)margin {
if (!margin.isNull())
margin_ = margin;
else
margin_ = default_margin_;
[self redraw];
}
- (NSRect)getButtonsContainerBounds {
return NSInsetRect([self getButtonsBounds], -margin_.x(), -margin_.y());
}
- (void)redraw {
NSView* titleBarContainer = [self titleBarContainer];
if (!titleBarContainer)
return;
NSView* left = [self leftButton];
NSView* middle = [self middleButton];
NSView* right = [self rightButton];
float button_width = NSWidth(left.frame);
float button_height = NSHeight(left.frame);
float padding = NSMinX(middle.frame) - NSMaxX(left.frame);
float start;
if (false /*base::i18n::IsRTL()*/)
start =
NSWidth(window_.frame) - 3 * button_width - 2 * padding - margin_.x();
else
start = margin_.x();
NSRect cbounds = titleBarContainer.frame;
cbounds.size.height = button_height + 2 * margin_.y();
cbounds.origin.y = NSHeight(window_.frame) - NSHeight(cbounds);
[titleBarContainer setFrame:cbounds];
[left setFrameOrigin:NSMakePoint(start, margin_.y())];
start += button_width + padding;
[middle setFrameOrigin:NSMakePoint(start, margin_.y())];
start += button_width + padding;
[right setFrameOrigin:NSMakePoint(start, margin_.y())];
if (hover_view_)
[hover_view_ setFrame:[self getButtonsBounds]];
}
- (void)updateTrackingAreas {
if (tracking_area_)
[hover_view_ removeTrackingArea:tracking_area_];
auto old_ptr = tracking_area_;
tracking_area_ = [[NSTrackingArea alloc]
initWithRect:NSZeroRect
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways |
NSTrackingInVisibleRect
owner:self
userInfo:nil];
[old_ptr release];
[hover_view_ addTrackingArea:tracking_area_];
}
- (void)mouseEntered:(NSEvent*)event {
mouse_inside_ = YES;
[self updateButtonsVisibility];
}
- (void)mouseExited:(NSEvent*)event {
mouse_inside_ = NO;
[self updateButtonsVisibility];
}
- (void)updateButtonsVisibility {
NSArray* buttons = @[
[window_ standardWindowButton:NSWindowCloseButton],
[window_ standardWindowButton:NSWindowMiniaturizeButton],
[window_ standardWindowButton:NSWindowZoomButton],
];
// Show buttons when mouse hovers above them.
BOOL hidden = show_on_hover_ && !mouse_inside_;
// Always show buttons under fullscreen.
if ([window_ styleMask] & NSWindowStyleMaskFullScreen)
hidden = NO;
for (NSView* button in buttons) {
[button setHidden:hidden];
[button setNeedsDisplay:YES];
}
}
// Return the bounds of all 3 buttons.
- (NSRect)getButtonsBounds {
NSView* left = [self leftButton];
NSView* right = [self rightButton];
return NSMakeRect(NSMinX(left.frame), NSMinY(left.frame),
NSMaxX(right.frame) - NSMinX(left.frame),
NSHeight(left.frame));
}
// Compute margin from position of current buttons.
- (QPoint)getCurrentMargin {
QPoint result;
NSView* titleBarContainer = [self titleBarContainer];
if (!titleBarContainer)
return result;
NSView* left = [self leftButton];
NSView* right = [self rightButton];
result.setX((NSHeight(titleBarContainer.frame) - NSHeight(left.frame)) / 2);
if (false /*base::i18n::IsRTL()*/)
result.setX(NSWidth(window_.frame) - NSMaxX(right.frame));
else
result.setX(NSMinX(left.frame));
return result;
}
// Receive the titlebar container, which might be nil if the window does not
// have the NSWindowStyleMaskTitled style.
- (NSView*)titleBarContainer {
NSView* left = [self leftButton];
if (!left.superview)
return nil;
return left.superview.superview;
}
// Receive the window buttons, note that the buttons might be removed and
// re-added on the fly so we should not cache them.
- (NSButton*)leftButton {
if (false /*base::i18n::IsRTL()*/)
return [window_ standardWindowButton:NSWindowZoomButton];
else
return [window_ standardWindowButton:NSWindowCloseButton];
}
- (NSButton*)middleButton {
return [window_ standardWindowButton:NSWindowMiniaturizeButton];
}
- (NSButton*)rightButton {
if (false /*base::i18n::IsRTL()*/)
return [window_ standardWindowButton:NSWindowCloseButton];
else
return [window_ standardWindowButton:NSWindowZoomButton];
}
@end