make mac window buttons location settable
This commit is contained in:
parent
92681dc9d9
commit
dcb5fb1da4
|
@ -71,4 +71,5 @@ Thumbs.db
|
|||
.qmake.conf
|
||||
*.res
|
||||
|
||||
.vscode/
|
||||
.vscode/
|
||||
*/.DS_Store
|
|
@ -106,7 +106,7 @@ void MainWindow::showEvent(QShowEvent *event)
|
|||
titleBarWidget->minimizeButton->hide();
|
||||
titleBarWidget->maximizeButton->hide();
|
||||
titleBarWidget->closeButton->hide();
|
||||
Utilities::showMacWindowButton(windowHandle());
|
||||
Utilities::setStandardWindowButtonsVisibility(windowHandle(), true);
|
||||
#endif // Q_OS_MAC
|
||||
inited = true;
|
||||
}
|
||||
|
|
|
@ -36,7 +36,10 @@ void FLWindow::initFramelessWindow()
|
|||
m_minimizeButton->hide();
|
||||
m_maximizeButton->hide();
|
||||
m_closeButton->hide();
|
||||
Utilities::showMacWindowButton(windowHandle());
|
||||
Utilities::setStandardWindowButtonsVisibility(windowHandle(), true);
|
||||
auto btnGroupSize = Utilities::standardWindowButtonsSize(windowHandle());
|
||||
Utilities::setStandardWindowButtonsPosition(windowHandle(),
|
||||
QPoint(12, (m_titleBarWidget->height() - btnGroupSize.height())/2));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -113,7 +113,7 @@ void Widget::showEvent(QShowEvent *event)
|
|||
m_minimizeButton->hide();
|
||||
m_maximizeButton->hide();
|
||||
m_closeButton->hide();
|
||||
Utilities::showMacWindowButton(windowHandle());
|
||||
Utilities::setStandardWindowButtonsVisibility(windowHandle(), true);
|
||||
#endif // Q_OS_MAC
|
||||
}
|
||||
}
|
||||
|
|
|
@ -26,8 +26,11 @@ else()
|
|||
if(APPLE)
|
||||
list(APPEND SOURCES
|
||||
core/utilities_macos.mm
|
||||
core/nswindow_proxy.h
|
||||
core/nswindow_proxy.mm
|
||||
core/window_buttons_proxy.h
|
||||
core/window_buttons_proxy.mm
|
||||
core/scoped_nsobject.h
|
||||
)
|
||||
else()
|
||||
find_package(Qt${QT_VERSION_MAJOR} COMPONENTS X11Extras REQUIRED)
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
#ifndef NSWINDOWPROXY_H
|
||||
#define NSWINDOWPROXY_H
|
||||
|
||||
#include <objc/runtime.h>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
#include <Quartz/Quartz.h>
|
||||
|
||||
#include <QtCore/qpoint.h>
|
||||
|
||||
#include "framelesshelper.h"
|
||||
#include "scoped_nsobject.h"
|
||||
#include "window_buttons_proxy.h"
|
||||
|
||||
@class NSWindowProxyDelegate;
|
||||
|
||||
class NSWindowProxy
|
||||
{
|
||||
private:
|
||||
NSWindow* m_window;
|
||||
scoped_nsobject<WindowButtonsProxy> m_buttonProxy;
|
||||
scoped_nsobject<NSWindowProxyDelegate> m_windowDelegate;
|
||||
bool m_windowButtonVisibility;
|
||||
QPoint m_trafficLightPosition;
|
||||
|
||||
public:
|
||||
NSWindowProxy(NSWindow *window);
|
||||
~NSWindowProxy();
|
||||
|
||||
NSWindow* window() { return m_window; }
|
||||
|
||||
QPoint trafficLightPosition() { return m_trafficLightPosition; }
|
||||
void setTrafficLightPosition(const QPoint &pos);
|
||||
|
||||
bool windowButtonVisibility() { return m_windowButtonVisibility; }
|
||||
void setWindowButtonVisibility(bool visible);
|
||||
|
||||
void redrawTrafficLights();
|
||||
|
||||
bool isFullscreen() const;
|
||||
void setTitle(const QString& title);
|
||||
|
||||
void notifyWindowEnterFullScreen();
|
||||
void notifyWindowLeaveFullScreen();
|
||||
void notifyWindowWillEnterFullScreen();
|
||||
void notifyWindowWillLeaveFullScreen();
|
||||
|
||||
};
|
||||
|
||||
@interface NSWindowProxyDelegate : NSObject<NSWindowDelegate> {
|
||||
@private
|
||||
NSWindowProxy* m_windowProxy;
|
||||
bool m_isZooming;
|
||||
int m_level;
|
||||
bool m_isResizable;
|
||||
|
||||
// Only valid during a live resize.
|
||||
// Used to keep track of whether a resize is happening horizontally or
|
||||
// vertically, even if physically the user is resizing in both directions.
|
||||
bool m_resizingHorizontally;
|
||||
}
|
||||
- (id)initWithWindowProxy:(NSWindowProxy*)proxy;
|
||||
@end
|
||||
|
||||
#endif // NSWINDOWPROXY_H
|
|
@ -0,0 +1,291 @@
|
|||
#include "nswindow_proxy.h"
|
||||
|
||||
#include <QtGui/qguiapplication.h>
|
||||
|
||||
static QList<NSWindow*> gFlsWindows;
|
||||
static bool gNSWindowOverrode = false;
|
||||
|
||||
typedef void (*setStyleMaskType)(id, SEL, NSWindowStyleMask);
|
||||
static setStyleMaskType gOrigSetStyleMask = nullptr;
|
||||
static void __setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask)
|
||||
{
|
||||
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
{
|
||||
styleMask = styleMask | NSWindowStyleMaskFullSizeContentView;
|
||||
}
|
||||
|
||||
if (gOrigSetStyleMask != nullptr)
|
||||
gOrigSetStyleMask(obj, sel, styleMask);
|
||||
}
|
||||
|
||||
typedef void (*setTitlebarAppearsTransparentType)(id, SEL, BOOL);
|
||||
static setTitlebarAppearsTransparentType gOrigSetTitlebarAppearsTransparent = nullptr;
|
||||
static void __setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent)
|
||||
{
|
||||
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
transparent = true;
|
||||
|
||||
if (gOrigSetTitlebarAppearsTransparent != nullptr)
|
||||
gOrigSetTitlebarAppearsTransparent(obj, sel, transparent);
|
||||
}
|
||||
|
||||
typedef BOOL (*canBecomeKeyWindowType)(id, SEL);
|
||||
static canBecomeKeyWindowType gOrigCanBecomeKeyWindow = nullptr;
|
||||
static BOOL __canBecomeKeyWindow(id obj, SEL sel)
|
||||
{
|
||||
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gOrigCanBecomeKeyWindow != nullptr)
|
||||
return gOrigCanBecomeKeyWindow(obj, sel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef BOOL (*canBecomeMainWindowType)(id, SEL);
|
||||
static canBecomeMainWindowType gOrigCanBecomeMainWindow = nullptr;
|
||||
static BOOL __canBecomeMainWindow(id obj, SEL sel)
|
||||
{
|
||||
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gOrigCanBecomeMainWindow != nullptr)
|
||||
return gOrigCanBecomeMainWindow(obj, sel);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef void (*sendEventType)(id, SEL, NSEvent*);
|
||||
static sendEventType gOrigSendEvent = nullptr;
|
||||
static void __sendEvent(id obj, SEL sel, NSEvent* event)
|
||||
{
|
||||
if (gOrigSendEvent != nullptr)
|
||||
gOrigSendEvent(obj, sel, event);
|
||||
|
||||
|
||||
if (!gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
return;
|
||||
|
||||
if (event.type == NSEventTypeLeftMouseDown)
|
||||
QGuiApplication::processEvents();
|
||||
}
|
||||
|
||||
typedef BOOL (*isFlippedType)(id, SEL);
|
||||
static isFlippedType gOrigIsFlipped = nullptr;
|
||||
static BOOL __isFlipped(id obj, SEL sel)
|
||||
{
|
||||
if (!gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
return true;
|
||||
|
||||
if (gOrigIsFlipped != nullptr)
|
||||
return gOrigIsFlipped(obj, sel);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
Replace origin method \a origSEL of class \a cls with new one \a newIMP ,
|
||||
then return old method as function pointer.
|
||||
*/
|
||||
static void* replaceMethod(Class cls, SEL origSEL, IMP newIMP)
|
||||
{
|
||||
Method origMethod = class_getInstanceMethod(cls, origSEL);
|
||||
void *funcPtr = (void *)method_getImplementation(origMethod);
|
||||
if (!class_addMethod(cls, origSEL, newIMP, method_getTypeEncoding(origMethod))) {
|
||||
method_setImplementation(origMethod, newIMP);
|
||||
}
|
||||
|
||||
return funcPtr;
|
||||
}
|
||||
|
||||
static void restoreMethod(Class cls, SEL origSEL, IMP oldIMP)
|
||||
{
|
||||
Method method = class_getInstanceMethod(cls, origSEL);
|
||||
method_setImplementation(method, oldIMP);
|
||||
}
|
||||
|
||||
static void overrideNSWindowMethods(NSWindow* window)
|
||||
{
|
||||
if (!gNSWindowOverrode) {
|
||||
Class cls = [window class];
|
||||
|
||||
gOrigSetStyleMask = (setStyleMaskType) replaceMethod(
|
||||
cls, @selector(setStyleMask:), (IMP) __setStyleMask);
|
||||
gOrigSetTitlebarAppearsTransparent = (setTitlebarAppearsTransparentType) replaceMethod(
|
||||
cls, @selector(setTitlebarAppearsTransparent:), (IMP) __setTitlebarAppearsTransparent);
|
||||
gOrigCanBecomeKeyWindow = (canBecomeKeyWindowType) replaceMethod(
|
||||
cls, @selector(canBecomeKeyWindow), (IMP) __canBecomeKeyWindow);
|
||||
gOrigCanBecomeMainWindow = (canBecomeMainWindowType) replaceMethod(
|
||||
cls, @selector(canBecomeMainWindow), (IMP) __canBecomeMainWindow);
|
||||
gOrigSendEvent = (sendEventType) replaceMethod(
|
||||
cls, @selector(sendEvent:), (IMP) __sendEvent);
|
||||
//gOrigIsFlipped = (isFlippedType) replaceMethod(
|
||||
// cls, @selector (isFlipped), (IMP) __isFlipped);
|
||||
|
||||
gNSWindowOverrode = true;
|
||||
}
|
||||
|
||||
gFlsWindows.append(window);
|
||||
}
|
||||
|
||||
static void restoreNSWindowMethods(NSWindow* window)
|
||||
{
|
||||
gFlsWindows.removeAll(window);
|
||||
if (gFlsWindows.size() == 0) {
|
||||
Class cls = [window class];
|
||||
|
||||
restoreMethod(cls, @selector(setStyleMask:), (IMP) gOrigSetStyleMask);
|
||||
gOrigSetStyleMask = nullptr;
|
||||
|
||||
restoreMethod(cls, @selector(setTitlebarAppearsTransparent:), (IMP) gOrigSetTitlebarAppearsTransparent);
|
||||
gOrigSetTitlebarAppearsTransparent = nullptr;
|
||||
|
||||
restoreMethod(cls, @selector(canBecomeKeyWindow), (IMP) gOrigCanBecomeKeyWindow);
|
||||
gOrigCanBecomeKeyWindow = nullptr;
|
||||
|
||||
restoreMethod(cls, @selector(canBecomeMainWindow), (IMP) gOrigCanBecomeMainWindow);
|
||||
gOrigCanBecomeMainWindow = nullptr;
|
||||
|
||||
restoreMethod(cls, @selector(sendEvent:), (IMP) gOrigSendEvent);
|
||||
gOrigSendEvent = nullptr;
|
||||
|
||||
//restoreMethod(cls, @selector(isFlipped), (IMP) gOrigIsFlipped);
|
||||
//gOrigIsFlipped = nullptr;
|
||||
|
||||
gNSWindowOverrode = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
NSWindowProxy::NSWindowProxy(NSWindow *window)
|
||||
: m_windowButtonVisibility(false)
|
||||
, m_buttonProxy(nullptr)
|
||||
, m_window(window)
|
||||
{
|
||||
overrideNSWindowMethods(window);
|
||||
m_buttonProxy.reset([[WindowButtonsProxy alloc] initWithWindow:window]);
|
||||
m_windowDelegate.reset([[NSWindowProxyDelegate alloc] initWithWindowProxy:this]);
|
||||
[m_window setDelegate:m_windowDelegate.get()];
|
||||
}
|
||||
|
||||
NSWindowProxy::~NSWindowProxy()
|
||||
{
|
||||
restoreNSWindowMethods(m_window);
|
||||
[m_buttonProxy release];
|
||||
}
|
||||
|
||||
void NSWindowProxy::setTrafficLightPosition(const QPoint &pos) {
|
||||
m_trafficLightPosition = pos;
|
||||
if (m_buttonProxy) {
|
||||
[m_buttonProxy setMargin:m_trafficLightPosition];
|
||||
}
|
||||
}
|
||||
|
||||
void NSWindowProxy::setWindowButtonVisibility(bool visible) {
|
||||
m_windowButtonVisibility = visible;
|
||||
// The visibility of window buttons are managed by |buttons_proxy_| if the
|
||||
// style is customButtonsOnHover.
|
||||
if (false /*title_bar_style_ == TitleBarStyle::kCustomButtonsOnHover*/)
|
||||
[m_buttonProxy setVisible:visible];
|
||||
else {
|
||||
[[m_window standardWindowButton:NSWindowCloseButton] setHidden:!visible];
|
||||
[[m_window standardWindowButton:NSWindowMiniaturizeButton] setHidden:!visible];
|
||||
[[m_window standardWindowButton:NSWindowZoomButton] setHidden:!visible];
|
||||
}
|
||||
}
|
||||
|
||||
bool NSWindowProxy::isFullscreen() const {
|
||||
return [m_window styleMask] & NSWindowStyleMaskFullScreen;
|
||||
}
|
||||
|
||||
void NSWindowProxy::redrawTrafficLights() {
|
||||
if (m_buttonProxy && !isFullscreen())
|
||||
[m_buttonProxy redraw];
|
||||
}
|
||||
|
||||
void NSWindowProxy::setTitle(const QString& title) {
|
||||
[m_window setTitle:title.toNSString()];
|
||||
if (m_buttonProxy)
|
||||
[m_buttonProxy redraw];
|
||||
}
|
||||
|
||||
void NSWindowProxy::notifyWindowEnterFullScreen() {
|
||||
// Restore the window title under fullscreen mode.
|
||||
if (m_buttonProxy) {
|
||||
[m_window setTitleVisibility:NSWindowTitleVisible];
|
||||
}
|
||||
}
|
||||
|
||||
void NSWindowProxy::notifyWindowLeaveFullScreen() {
|
||||
// Restore window buttons.
|
||||
if (m_buttonProxy && m_windowButtonVisibility) {
|
||||
[m_buttonProxy redraw];
|
||||
[m_buttonProxy setVisible:YES];
|
||||
}
|
||||
}
|
||||
|
||||
void NSWindowProxy::notifyWindowWillEnterFullScreen() {
|
||||
|
||||
}
|
||||
|
||||
void NSWindowProxy::notifyWindowWillLeaveFullScreen() {
|
||||
if (m_buttonProxy) {
|
||||
// Hide window title when leaving fullscreen.
|
||||
[m_window setTitleVisibility:NSWindowTitleHidden];
|
||||
// Hide the container otherwise traffic light buttons jump.
|
||||
[m_buttonProxy setVisible:NO];
|
||||
}
|
||||
}
|
||||
|
||||
@implementation NSWindowProxyDelegate
|
||||
- (id)initWithWindowProxy:(NSWindowProxy*)proxy {
|
||||
m_windowProxy = proxy;
|
||||
return self;
|
||||
}
|
||||
|
||||
- (void)windowDidBecomeMain:(NSNotification*)notification {
|
||||
m_windowProxy->redrawTrafficLights();
|
||||
}
|
||||
|
||||
- (void)windowDidResignMain:(NSNotification*)notification {
|
||||
m_windowProxy->redrawTrafficLights();
|
||||
}
|
||||
|
||||
- (void)windowDidBecomeKey:(NSNotification*)notification {
|
||||
m_windowProxy->redrawTrafficLights();
|
||||
}
|
||||
|
||||
- (void)windowDidResignKey:(NSNotification*)notification {
|
||||
// If our app is still active and we're still the key window, ignore this
|
||||
// message, since it just means that a menu extra (on the "system status bar")
|
||||
// was activated; we'll get another |-windowDidResignKey| if we ever really
|
||||
// lose key window status.
|
||||
if ([NSApp isActive] && ([NSApp keyWindow] == [notification object]))
|
||||
return;
|
||||
|
||||
m_windowProxy->redrawTrafficLights();
|
||||
}
|
||||
|
||||
- (void)windowDidResize:(NSNotification*)notification {
|
||||
m_windowProxy->redrawTrafficLights();
|
||||
}
|
||||
|
||||
- (void)windowWillEnterFullScreen:(NSNotification*)notification {
|
||||
m_windowProxy->notifyWindowWillEnterFullScreen();
|
||||
}
|
||||
|
||||
- (void)windowDidEnterFullScreen:(NSNotification*)notification {
|
||||
m_windowProxy->notifyWindowEnterFullScreen();
|
||||
}
|
||||
|
||||
- (void)windowWillExitFullScreen:(NSNotification*)notification {
|
||||
m_windowProxy->notifyWindowWillLeaveFullScreen();
|
||||
}
|
||||
|
||||
- (void)windowDidExitFullScreen:(NSNotification*)notification {
|
||||
m_windowProxy->notifyWindowLeaveFullScreen();
|
||||
}
|
||||
@end
|
|
@ -0,0 +1,203 @@
|
|||
// Copyright 2013 The Flutter Authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
#ifndef SCOPED_NSOBJECT_H
|
||||
#define SCOPED_NSOBJECT_H
|
||||
|
||||
// Include NSObject.h directly because Foundation.h pulls in many dependencies.
|
||||
// (Approx 100k lines of code versus 1.5k for NSObject.h). scoped_nsobject gets
|
||||
// singled out because it is most typically included from other header files.
|
||||
#import <Foundation/NSObject.h>
|
||||
|
||||
#if !defined(__GNUC__) && !defined(__clang__) && !defined(_MSC_VER)
|
||||
#error Unsupported compiler.
|
||||
#endif
|
||||
|
||||
// Annotate a variable indicating it's ok if the variable is not used.
|
||||
// (Typically used to silence a compiler warning when the assignment
|
||||
// is important for some other reason.)
|
||||
// Use like:
|
||||
// int x = ...;
|
||||
// FML_ALLOW_UNUSED_LOCAL(x);
|
||||
#define FML_ALLOW_UNUSED_LOCAL(x) false ? (void)x : (void)0
|
||||
|
||||
// Annotate a typedef or function indicating it's ok if it's not used.
|
||||
// Use like:
|
||||
// typedef Foo Bar ALLOW_UNUSED_TYPE;
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define FML_ALLOW_UNUSED_TYPE __attribute__((unused))
|
||||
#else
|
||||
#define FML_ALLOW_UNUSED_TYPE
|
||||
#endif
|
||||
|
||||
#ifndef FML_USED_ON_EMBEDDER
|
||||
|
||||
#define FML_EMBEDDER_ONLY [[deprecated]]
|
||||
|
||||
#else // FML_USED_ON_EMBEDDER
|
||||
|
||||
#define FML_EMBEDDER_ONLY
|
||||
|
||||
#endif // FML_USED_ON_EMBEDDER
|
||||
|
||||
#define FML_DISALLOW_COPY(TypeName) TypeName(const TypeName&) = delete
|
||||
|
||||
#define FML_DISALLOW_ASSIGN(TypeName) \
|
||||
TypeName& operator=(const TypeName&) = delete
|
||||
|
||||
#define FML_DISALLOW_MOVE(TypeName) \
|
||||
TypeName(TypeName&&) = delete; \
|
||||
TypeName& operator=(TypeName&&) = delete
|
||||
|
||||
#define FML_DISALLOW_COPY_AND_ASSIGN(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
TypeName& operator=(const TypeName&) = delete
|
||||
|
||||
#define FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TypeName) \
|
||||
TypeName(const TypeName&) = delete; \
|
||||
TypeName(TypeName&&) = delete; \
|
||||
TypeName& operator=(const TypeName&) = delete; \
|
||||
TypeName& operator=(TypeName&&) = delete
|
||||
|
||||
#define FML_DISALLOW_IMPLICIT_CONSTRUCTORS(TypeName) \
|
||||
TypeName() = delete; \
|
||||
FML_DISALLOW_COPY_ASSIGN_AND_MOVE(TypeName)
|
||||
|
||||
@class NSAutoreleasePool;
|
||||
|
||||
// scoped_nsobject<> is patterned after scoped_ptr<>, but maintains ownership
|
||||
// of an NSObject subclass object. Style deviations here are solely for
|
||||
// compatibility with scoped_ptr<>'s interface, with which everyone is already
|
||||
// familiar.
|
||||
//
|
||||
// scoped_nsobject<> takes ownership of an object (in the constructor or in
|
||||
// reset()) by taking over the caller's existing ownership claim. The caller
|
||||
// must own the object it gives to scoped_nsobject<>, and relinquishes an
|
||||
// ownership claim to that object. scoped_nsobject<> does not call -retain,
|
||||
// callers have to call this manually if appropriate.
|
||||
//
|
||||
// scoped_nsprotocol<> has the same behavior as scoped_nsobject, but can be used
|
||||
// with protocols.
|
||||
//
|
||||
// scoped_nsobject<> is not to be used for NSAutoreleasePools. For
|
||||
// NSAutoreleasePools use ScopedNSAutoreleasePool from
|
||||
// scoped_nsautorelease_pool.h instead.
|
||||
// We check for bad uses of scoped_nsobject and NSAutoreleasePool at compile
|
||||
// time with a template specialization (see below).
|
||||
|
||||
template <typename NST>
|
||||
class scoped_nsprotocol {
|
||||
public:
|
||||
explicit scoped_nsprotocol(NST object = nil) : object_(object) {}
|
||||
|
||||
scoped_nsprotocol(const scoped_nsprotocol<NST>& that) : object_([that.object_ retain]) {}
|
||||
|
||||
template <typename NSU>
|
||||
scoped_nsprotocol(const scoped_nsprotocol<NSU>& that) : object_([that.get() retain]) {}
|
||||
|
||||
~scoped_nsprotocol() { [object_ release]; }
|
||||
|
||||
scoped_nsprotocol& operator=(const scoped_nsprotocol<NST>& that) {
|
||||
reset([that.get() retain]);
|
||||
return *this;
|
||||
}
|
||||
|
||||
void reset(NST object = nil) {
|
||||
// We intentionally do not check that object != object_ as the caller must
|
||||
// either already have an ownership claim over whatever it passes to this
|
||||
// method, or call it with the |RETAIN| policy which will have ensured that
|
||||
// the object is retained once more when reaching this point.
|
||||
[object_ release];
|
||||
object_ = object;
|
||||
}
|
||||
|
||||
bool operator==(NST that) const { return object_ == that; }
|
||||
bool operator!=(NST that) const { return object_ != that; }
|
||||
|
||||
operator NST() const { return object_; }
|
||||
|
||||
NST get() const { return object_; }
|
||||
|
||||
void swap(scoped_nsprotocol& that) {
|
||||
NST temp = that.object_;
|
||||
that.object_ = object_;
|
||||
object_ = temp;
|
||||
}
|
||||
|
||||
// Shift reference to the autorelease pool to be released later.
|
||||
NST autorelease() { return [release() autorelease]; }
|
||||
|
||||
private:
|
||||
NST object_;
|
||||
|
||||
// scoped_nsprotocol<>::release() is like scoped_ptr<>::release. It is NOT a
|
||||
// wrapper for [object_ release]. To force a scoped_nsprotocol<> to call
|
||||
// [object_ release], use scoped_nsprotocol<>::reset().
|
||||
[[nodiscard]] NST release() {
|
||||
NST temp = object_;
|
||||
object_ = nil;
|
||||
return temp;
|
||||
}
|
||||
};
|
||||
|
||||
// Free functions
|
||||
template <class C>
|
||||
void swap(scoped_nsprotocol<C>& p1, scoped_nsprotocol<C>& p2) {
|
||||
p1.swap(p2);
|
||||
}
|
||||
|
||||
template <class C>
|
||||
bool operator==(C p1, const scoped_nsprotocol<C>& p2) {
|
||||
return p1 == p2.get();
|
||||
}
|
||||
|
||||
template <class C>
|
||||
bool operator!=(C p1, const scoped_nsprotocol<C>& p2) {
|
||||
return p1 != p2.get();
|
||||
}
|
||||
|
||||
template <typename NST>
|
||||
class scoped_nsobject : public scoped_nsprotocol<NST*> {
|
||||
public:
|
||||
explicit scoped_nsobject(NST* object = nil) : scoped_nsprotocol<NST*>(object) {}
|
||||
|
||||
scoped_nsobject(const scoped_nsobject<NST>& that) : scoped_nsprotocol<NST*>(that) {}
|
||||
|
||||
template <typename NSU>
|
||||
scoped_nsobject(const scoped_nsobject<NSU>& that) : scoped_nsprotocol<NST*>(that) {}
|
||||
|
||||
scoped_nsobject& operator=(const scoped_nsobject<NST>& that) {
|
||||
scoped_nsprotocol<NST*>::operator=(that);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Specialization to make scoped_nsobject<id> work.
|
||||
template <>
|
||||
class scoped_nsobject<id> : public scoped_nsprotocol<id> {
|
||||
public:
|
||||
explicit scoped_nsobject(id object = nil) : scoped_nsprotocol<id>(object) {}
|
||||
|
||||
scoped_nsobject(const scoped_nsobject<id>& that) : scoped_nsprotocol<id>(that) {}
|
||||
|
||||
template <typename NSU>
|
||||
scoped_nsobject(const scoped_nsobject<NSU>& that) : scoped_nsprotocol<id>(that) {}
|
||||
|
||||
scoped_nsobject& operator=(const scoped_nsobject<id>& that) {
|
||||
scoped_nsprotocol<id>::operator=(that);
|
||||
return *this;
|
||||
}
|
||||
};
|
||||
|
||||
// Do not use scoped_nsobject for NSAutoreleasePools, use
|
||||
// ScopedNSAutoreleasePool instead. This is a compile time check. See details
|
||||
// at top of header.
|
||||
template <>
|
||||
class scoped_nsobject<NSAutoreleasePool> {
|
||||
private:
|
||||
explicit scoped_nsobject(NSAutoreleasePool* object = nil);
|
||||
FML_DISALLOW_COPY_AND_ASSIGN(scoped_nsobject);
|
||||
};
|
||||
|
||||
#endif // SCOPED_NSOBJECT_H
|
|
@ -26,6 +26,7 @@
|
|||
|
||||
#include "framelesshelper_global.h"
|
||||
#include <QtGui/qwindow.h>
|
||||
#include <QtCore/qsize.h>
|
||||
|
||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
|
||||
|
@ -75,13 +76,9 @@ FRAMELESSHELPER_API bool setMacWindowFrameless(QWindow* w);
|
|||
FRAMELESSHELPER_API bool unsetMacWindowFrameless(QWindow* w);
|
||||
FRAMELESSHELPER_API bool startMacDrag(QWindow* w, const QPoint& pos);
|
||||
FRAMELESSHELPER_API Qt::MouseButtons getMacMouseButtons();
|
||||
FRAMELESSHELPER_API bool showMacWindowButton(QWindow *w);
|
||||
FRAMELESSHELPER_API bool isMacKeyWindow(QWindow *w);
|
||||
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);
|
||||
FRAMELESSHELPER_API bool setStandardWindowButtonsVisibility(QWindow *w, bool visible);
|
||||
FRAMELESSHELPER_API bool setStandardWindowButtonsPosition(QWindow *w, const QPoint &pos);
|
||||
FRAMELESSHELPER_API QSize standardWindowButtonsSize(QWindow *w);
|
||||
#endif // Q_OS_MAC
|
||||
|
||||
}
|
||||
|
|
|
@ -24,15 +24,11 @@
|
|||
|
||||
#include "utilities.h"
|
||||
|
||||
#include <objc/runtime.h>
|
||||
#include <Cocoa/Cocoa.h>
|
||||
#include <Carbon/Carbon.h>
|
||||
|
||||
#include <QtGui/qguiapplication.h>
|
||||
#include <QtCore/qlist.h>
|
||||
#include <QtCore/qdebug.h>
|
||||
|
||||
#include "window_buttons_proxy.h"
|
||||
#include "nswindow_proxy.h"
|
||||
|
||||
FRAMELESSHELPER_BEGIN_NAMESPACE
|
||||
|
||||
|
@ -123,201 +119,7 @@ bool showSystemMenu(const WId winId, const QPointF &pos)
|
|||
return false;
|
||||
}
|
||||
|
||||
static QList<NSWindow*> gFlsWindows;
|
||||
static bool gNSWindowOverrode = false;
|
||||
|
||||
typedef void (*setStyleMaskType)(id, SEL, NSWindowStyleMask);
|
||||
static setStyleMaskType gOrigSetStyleMask = nullptr;
|
||||
static void __setStyleMask(id obj, SEL sel, NSWindowStyleMask styleMask)
|
||||
{
|
||||
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
{
|
||||
styleMask = styleMask | NSWindowStyleMaskFullSizeContentView;
|
||||
}
|
||||
|
||||
if (gOrigSetStyleMask != nullptr)
|
||||
gOrigSetStyleMask(obj, sel, styleMask);
|
||||
}
|
||||
|
||||
typedef void (*setTitlebarAppearsTransparentType)(id, SEL, BOOL);
|
||||
static setTitlebarAppearsTransparentType gOrigSetTitlebarAppearsTransparent = nullptr;
|
||||
static void __setTitlebarAppearsTransparent(id obj, SEL sel, BOOL transparent)
|
||||
{
|
||||
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
transparent = true;
|
||||
|
||||
if (gOrigSetTitlebarAppearsTransparent != nullptr)
|
||||
gOrigSetTitlebarAppearsTransparent(obj, sel, transparent);
|
||||
}
|
||||
|
||||
typedef BOOL (*canBecomeKeyWindowType)(id, SEL);
|
||||
static canBecomeKeyWindowType gOrigCanBecomeKeyWindow = nullptr;
|
||||
static BOOL __canBecomeKeyWindow(id obj, SEL sel)
|
||||
{
|
||||
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gOrigCanBecomeKeyWindow != nullptr)
|
||||
return gOrigCanBecomeKeyWindow(obj, sel);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef BOOL (*canBecomeMainWindowType)(id, SEL);
|
||||
static canBecomeMainWindowType gOrigCanBecomeMainWindow = nullptr;
|
||||
static BOOL __canBecomeMainWindow(id obj, SEL sel)
|
||||
{
|
||||
if (gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
if (gOrigCanBecomeMainWindow != nullptr)
|
||||
return gOrigCanBecomeMainWindow(obj, sel);
|
||||
return true;
|
||||
}
|
||||
|
||||
typedef void (*sendEventType)(id, SEL, NSEvent*);
|
||||
static sendEventType gOrigSendEvent = nullptr;
|
||||
static void __sendEvent(id obj, SEL sel, NSEvent* event)
|
||||
{
|
||||
if (gOrigSendEvent != nullptr)
|
||||
gOrigSendEvent(obj, sel, event);
|
||||
|
||||
|
||||
if (!gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
return;
|
||||
|
||||
if (event.type == NSEventTypeLeftMouseDown)
|
||||
QGuiApplication::processEvents();
|
||||
}
|
||||
|
||||
typedef BOOL (*isFlippedType)(id, SEL);
|
||||
static isFlippedType gOrigIsFlipped = nullptr;
|
||||
static BOOL __isFlipped(id obj, SEL sel)
|
||||
{
|
||||
if (!gFlsWindows.contains(reinterpret_cast<NSWindow *>(obj)))
|
||||
return true;
|
||||
|
||||
if (gOrigIsFlipped != nullptr)
|
||||
return gOrigIsFlipped(obj, sel);
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/*!
|
||||
Replace origin method \a origSEL of class \a cls with new one \a newIMP ,
|
||||
then return old method as function pointer.
|
||||
*/
|
||||
static void* replaceMethod(Class cls, SEL origSEL, IMP newIMP)
|
||||
{
|
||||
Method origMethod = class_getInstanceMethod(cls, origSEL);
|
||||
void *funcPtr = (void *)method_getImplementation(origMethod);
|
||||
if (!class_addMethod(cls, origSEL, newIMP, method_getTypeEncoding(origMethod))) {
|
||||
method_setImplementation(origMethod, newIMP);
|
||||
}
|
||||
|
||||
return funcPtr;
|
||||
}
|
||||
|
||||
static void restoreMethod(Class cls, SEL origSEL, IMP oldIMP)
|
||||
{
|
||||
Method method = class_getInstanceMethod(cls, origSEL);
|
||||
method_setImplementation(method, oldIMP);
|
||||
}
|
||||
|
||||
static void overrideNSWindowMethods(NSWindow* window)
|
||||
{
|
||||
if (!gNSWindowOverrode) {
|
||||
Class cls = [window class];
|
||||
|
||||
gOrigSetStyleMask = (setStyleMaskType) replaceMethod(
|
||||
cls, @selector(setStyleMask:), (IMP) __setStyleMask);
|
||||
gOrigSetTitlebarAppearsTransparent = (setTitlebarAppearsTransparentType) replaceMethod(
|
||||
cls, @selector(setTitlebarAppearsTransparent:), (IMP) __setTitlebarAppearsTransparent);
|
||||
gOrigCanBecomeKeyWindow = (canBecomeKeyWindowType) replaceMethod(
|
||||
cls, @selector(canBecomeKeyWindow), (IMP) __canBecomeKeyWindow);
|
||||
gOrigCanBecomeMainWindow = (canBecomeMainWindowType) replaceMethod(
|
||||
cls, @selector(canBecomeMainWindow), (IMP) __canBecomeMainWindow);
|
||||
gOrigSendEvent = (sendEventType) replaceMethod(
|
||||
cls, @selector(sendEvent:), (IMP) __sendEvent);
|
||||
//gOrigIsFlipped = (isFlippedType) replaceMethod(
|
||||
// cls, @selector (isFlipped), (IMP) __isFlipped);
|
||||
|
||||
gNSWindowOverrode = true;
|
||||
}
|
||||
|
||||
gFlsWindows.append(window);
|
||||
}
|
||||
|
||||
static void restoreNSWindowMethods(NSWindow* window)
|
||||
{
|
||||
gFlsWindows.removeAll(window);
|
||||
if (gFlsWindows.size() == 0) {
|
||||
Class cls = [window class];
|
||||
|
||||
restoreMethod(cls, @selector(setStyleMask:), (IMP) gOrigSetStyleMask);
|
||||
gOrigSetStyleMask = nullptr;
|
||||
|
||||
restoreMethod(cls, @selector(setTitlebarAppearsTransparent:), (IMP) gOrigSetTitlebarAppearsTransparent);
|
||||
gOrigSetTitlebarAppearsTransparent = nullptr;
|
||||
|
||||
restoreMethod(cls, @selector(canBecomeKeyWindow), (IMP) gOrigCanBecomeKeyWindow);
|
||||
gOrigCanBecomeKeyWindow = nullptr;
|
||||
|
||||
restoreMethod(cls, @selector(canBecomeMainWindow), (IMP) gOrigCanBecomeMainWindow);
|
||||
gOrigCanBecomeMainWindow = nullptr;
|
||||
|
||||
restoreMethod(cls, @selector(sendEvent:), (IMP) gOrigSendEvent);
|
||||
gOrigSendEvent = nullptr;
|
||||
|
||||
//restoreMethod(cls, @selector(isFlipped), (IMP) gOrigIsFlipped);
|
||||
//gOrigIsFlipped = nullptr;
|
||||
|
||||
gNSWindowOverrode = false;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
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 QHash<QWindow*, NSWindowProxy*> gQWindowToNSWindow;
|
||||
|
||||
static NSWindow* getNSWindow(QWindow* w)
|
||||
{
|
||||
|
@ -341,7 +143,7 @@ bool setMacWindowHook(QWindow* w)
|
|||
if (nswindow == nullptr)
|
||||
return false;
|
||||
|
||||
NSWindowData *obj = new NSWindowData(nswindow);
|
||||
NSWindowProxy *obj = new NSWindowProxy(nswindow);
|
||||
gQWindowToNSWindow.insert(w, obj);
|
||||
|
||||
return true;
|
||||
|
@ -352,7 +154,7 @@ bool unsetMacWindowHook(QWindow* w)
|
|||
if (!gQWindowToNSWindow.contains(w))
|
||||
return false;
|
||||
|
||||
NSWindowData* obj = gQWindowToNSWindow[w];
|
||||
NSWindowProxy* obj = gQWindowToNSWindow[w];
|
||||
gQWindowToNSWindow.remove(w);
|
||||
delete obj;
|
||||
|
||||
|
@ -414,20 +216,6 @@ bool unsetMacWindowFrameless(QWindow* w)
|
|||
return true;
|
||||
}
|
||||
|
||||
bool showMacWindowButton(QWindow *w)
|
||||
{
|
||||
NSWindow* nswindow = getNSWindow(w);
|
||||
if (nswindow == nullptr)
|
||||
return false;
|
||||
|
||||
nswindow.showsToolbarButton = true;
|
||||
[nswindow standardWindowButton:NSWindowCloseButton].hidden = false;
|
||||
[nswindow standardWindowButton:NSWindowMiniaturizeButton].hidden = false;
|
||||
[nswindow standardWindowButton:NSWindowZoomButton].hidden = false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool startMacDrag(QWindow* w, const QPoint& pos)
|
||||
{
|
||||
NSWindow* nswindow = getNSWindow(w);
|
||||
|
@ -447,89 +235,33 @@ Qt::MouseButtons getMacMouseButtons()
|
|||
return static_cast<Qt::MouseButtons>((uint)(NSEvent.pressedMouseButtons & Qt::MouseButtonMask));
|
||||
}
|
||||
|
||||
bool isMacKeyWindow(QWindow *w)
|
||||
bool setStandardWindowButtonsVisibility(QWindow *w, bool visible)
|
||||
{
|
||||
NSWindow* nswindow = getNSWindow(w);
|
||||
if (nswindow == nullptr) {
|
||||
qWarning() << "Unable to get NSView.";
|
||||
return false;
|
||||
}
|
||||
|
||||
return [nswindow isKeyWindow];
|
||||
}
|
||||
|
||||
bool isMacMainWindow(QWindow *w)
|
||||
{
|
||||
NSWindow* nswindow = getNSWindow(w);
|
||||
if (nswindow == nullptr)
|
||||
return false;
|
||||
|
||||
return [nswindow isMainWindow];
|
||||
}
|
||||
|
||||
bool makeMacKeyWindow(QWindow *w)
|
||||
{
|
||||
NSWindow* nswindow = getNSWindow(w);
|
||||
if (nswindow == nullptr)
|
||||
return false;
|
||||
|
||||
[nswindow makeKeyWindow];
|
||||
NSWindowProxy* obj = gQWindowToNSWindow[w];
|
||||
obj->setWindowButtonVisibility(visible);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool makeMacMainWindow(QWindow *w)
|
||||
/*! The origin of \a pos is top-left of window. */
|
||||
bool setStandardWindowButtonsPosition(QWindow *w, const QPoint &pos)
|
||||
{
|
||||
NSWindow* nswindow = getNSWindow(w);
|
||||
if (nswindow == nullptr)
|
||||
return false;
|
||||
|
||||
[nswindow makeMainWindow];
|
||||
NSWindowProxy* obj = gQWindowToNSWindow[w];
|
||||
obj->setWindowButtonVisibility(true);
|
||||
obj->setTrafficLightPosition(pos);
|
||||
return true;
|
||||
}
|
||||
|
||||
static void setButtonLocation(NSButton * btn, const QPoint &offset, NSView *contentview)
|
||||
{
|
||||
if (btn.superview != contentview) {
|
||||
[btn.superview willRemoveSubview:btn];
|
||||
[btn removeFromSuperview];
|
||||
[btn viewWillMoveToSuperview:contentview];
|
||||
[contentview addSubview:btn];
|
||||
[btn viewDidMoveToSuperview];
|
||||
}
|
||||
|
||||
auto frame = btn.frame;
|
||||
btn.frame = NSMakeRect(
|
||||
frame.origin.x + offset.x(),
|
||||
frame.origin.y + offset.y(),
|
||||
frame.size.width, frame.size.height);
|
||||
}
|
||||
|
||||
/*! The origin of AppKit coordinate system is bottom-left. */
|
||||
bool setStandardWindowButtonsOffset(QWindow *w, const QPoint &offset)
|
||||
QSize standardWindowButtonsSize(QWindow *w)
|
||||
{
|
||||
NSWindow* nswindow = getNSWindow(w);
|
||||
if (nswindow == nullptr)
|
||||
return false;
|
||||
return QSize();
|
||||
|
||||
NSView* contentview = nswindow.contentView;
|
||||
if (contentview == nullptr)
|
||||
return false;
|
||||
|
||||
auto close = [nswindow standardWindowButton:NSWindowCloseButton];
|
||||
auto min = [nswindow standardWindowButton:NSWindowMiniaturizeButton];
|
||||
auto zoom = [nswindow standardWindowButton:NSWindowZoomButton];
|
||||
|
||||
setButtonLocation(close, offset, contentview);
|
||||
setButtonLocation(min, offset, contentview);
|
||||
setButtonLocation(zoom, offset, contentview);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool setTrafficLightPosition(QWindow *w, const QPoint &pos)
|
||||
{
|
||||
NSWindowData* obj = gQWindowToNSWindow[w];
|
||||
return obj->setTrafficLightPosition(pos);
|
||||
NSButton* left = [nswindow standardWindowButton:NSWindowCloseButton];
|
||||
NSButton* right = [nswindow standardWindowButton:NSWindowZoomButton];
|
||||
float height = NSHeight(left.frame);
|
||||
float width = NSMaxX(right.frame) - NSMinX(left.frame);
|
||||
return QSize(width, height);
|
||||
}
|
||||
|
||||
} // namespace Utilities
|
||||
|
|
|
@ -9,6 +9,8 @@
|
|||
#import <Cocoa/Cocoa.h>
|
||||
#include <QtCore/qpoint.h>
|
||||
|
||||
#include "scoped_nsobject.h"
|
||||
|
||||
@class WindowButtonsProxy;
|
||||
|
||||
// A helper view that floats above the window buttons.
|
||||
|
@ -32,8 +34,8 @@
|
|||
// Track mouse moves above window buttons.
|
||||
BOOL show_on_hover_;
|
||||
BOOL mouse_inside_;
|
||||
NSTrackingArea* tracking_area_;
|
||||
ButtonsAreaHoverView* hover_view_;
|
||||
scoped_nsobject<NSTrackingArea> tracking_area_;
|
||||
scoped_nsobject<ButtonsAreaHoverView> hover_view_;
|
||||
}
|
||||
|
||||
- (id)initWithWindow:(NSWindow*)window;
|
||||
|
|
|
@ -65,15 +65,12 @@
|
|||
// 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_.reset([[ButtonsAreaHoverView alloc] initWithProxy:self]);
|
||||
[hover_view_ setFrame:[self getButtonsBounds]];
|
||||
[titleBarContainer addSubview:hover_view_];
|
||||
[titleBarContainer addSubview:hover_view_.get()];
|
||||
} else {
|
||||
[hover_view_ removeFromSuperview];
|
||||
[hover_view_ release];
|
||||
hover_view_ = nullptr;
|
||||
hover_view_.reset();
|
||||
}
|
||||
[self updateButtonsVisibility];
|
||||
}
|
||||
|
@ -126,16 +123,14 @@
|
|||
|
||||
- (void)updateTrackingAreas {
|
||||
if (tracking_area_)
|
||||
[hover_view_ removeTrackingArea:tracking_area_];
|
||||
auto old_ptr = tracking_area_;
|
||||
tracking_area_ = [[NSTrackingArea alloc]
|
||||
[hover_view_ removeTrackingArea:tracking_area_.get()];
|
||||
tracking_area_.reset([[NSTrackingArea alloc]
|
||||
initWithRect:NSZeroRect
|
||||
options:NSTrackingMouseEnteredAndExited | NSTrackingActiveAlways |
|
||||
NSTrackingInVisibleRect
|
||||
owner:self
|
||||
userInfo:nil];
|
||||
[old_ptr release];
|
||||
[hover_view_ addTrackingArea:tracking_area_];
|
||||
userInfo:nil]);
|
||||
[hover_view_ addTrackingArea:tracking_area_.get()];
|
||||
}
|
||||
|
||||
- (void)mouseEntered:(NSEvent*)event {
|
||||
|
|
Loading…
Reference in New Issue