mirror of
https://github.com/mozilla/gecko-dev.git
synced 2024-10-09 03:15:11 +00:00
Bug 647216: Allow mouse event handling in the title bar on OS X. A number of contributors to this patch, including Markus Stange, Paul O'Shannessy, Mike Conley, Stephen Pohl, and myself (Josh Aas). r=mstange,dao,enndeakin
This commit is contained in:
parent
dd7a85d6a7
commit
41e8b6b37d
@ -36,7 +36,7 @@ ifneq (,$(filter windows cocoa gtk2, $(MOZ_WIDGET_TOOLKIT)))
|
||||
DEFINES += -DCONTEXT_COPY_IMAGE_CONTENTS=1
|
||||
endif
|
||||
|
||||
ifneq (,$(filter windows, $(MOZ_WIDGET_TOOLKIT)))
|
||||
ifneq (,$(filter windows cocoa, $(MOZ_WIDGET_TOOLKIT)))
|
||||
DEFINES += -DCAN_DRAW_IN_TITLEBAR=1
|
||||
endif
|
||||
|
||||
|
@ -489,6 +489,7 @@
|
||||
#ifdef CAN_DRAW_IN_TITLEBAR
|
||||
<vbox id="titlebar">
|
||||
<hbox id="titlebar-content">
|
||||
#ifdef MENUBAR_CAN_AUTOHIDE
|
||||
<hbox id="appmenu-button-container">
|
||||
<button id="appmenu-button"
|
||||
type="menu"
|
||||
@ -497,6 +498,7 @@
|
||||
#include browser-appmenu.inc
|
||||
</button>
|
||||
</hbox>
|
||||
#endif
|
||||
<spacer id="titlebar-spacer" flex="1"/>
|
||||
<hbox id="titlebar-buttonbox-container" align="start">
|
||||
<hbox id="titlebar-buttonbox">
|
||||
|
@ -38,6 +38,15 @@
|
||||
background-color: #eeeeee;
|
||||
}
|
||||
|
||||
#titlebar-buttonbox-container,
|
||||
#titlebar:not(:-moz-lwtheme) {
|
||||
display: none;
|
||||
}
|
||||
|
||||
#main-window[drawintitlebar="true"] > #titlebar {
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
#main-window[chromehidden~="toolbar"][chromehidden~="location"][chromehidden~="directories"] {
|
||||
border-top: 1px solid rgba(0,0,0,0.65);
|
||||
}
|
||||
|
@ -560,11 +560,12 @@ nsDOMWindowUtils::SendMouseEvent(const nsAString& aType,
|
||||
int32_t aModifiers,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
float aPressure,
|
||||
unsigned short aInputSourceArg)
|
||||
unsigned short aInputSourceArg,
|
||||
bool *aPreventDefault)
|
||||
{
|
||||
return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
|
||||
aIgnoreRootScrollFrame, aPressure,
|
||||
aInputSourceArg, false);
|
||||
aInputSourceArg, false, aPreventDefault);
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
@ -581,7 +582,7 @@ nsDOMWindowUtils::SendMouseEventToWindow(const nsAString& aType,
|
||||
SAMPLE_LABEL("nsDOMWindowUtils", "SendMouseEventToWindow");
|
||||
return SendMouseEventCommon(aType, aX, aY, aButton, aClickCount, aModifiers,
|
||||
aIgnoreRootScrollFrame, aPressure,
|
||||
aInputSourceArg, true);
|
||||
aInputSourceArg, true, nullptr);
|
||||
}
|
||||
|
||||
static nsIntPoint
|
||||
@ -604,7 +605,8 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
|
||||
bool aIgnoreRootScrollFrame,
|
||||
float aPressure,
|
||||
unsigned short aInputSourceArg,
|
||||
bool aToWindow)
|
||||
bool aToWindow,
|
||||
bool *aPreventDefault)
|
||||
{
|
||||
if (!nsContentUtils::IsCallerChrome()) {
|
||||
return NS_ERROR_DOM_SECURITY_ERR;
|
||||
@ -631,7 +633,9 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
|
||||
else if (aType.EqualsLiteral("contextmenu")) {
|
||||
msg = NS_CONTEXTMENU;
|
||||
contextMenuKey = (aButton == 0);
|
||||
} else
|
||||
} else if (aType.EqualsLiteral("MozMouseHittest"))
|
||||
msg = NS_MOUSE_MOZHITTEST;
|
||||
else
|
||||
return NS_ERROR_FAILURE;
|
||||
|
||||
if (aInputSourceArg == nsIDOMMouseEvent::MOZ_SOURCE_UNKNOWN) {
|
||||
@ -672,7 +676,10 @@ nsDOMWindowUtils::SendMouseEventCommon(const nsAString& aType,
|
||||
status = nsEventStatus_eIgnore;
|
||||
return presShell->HandleEvent(view->GetFrame(), &event, false, &status);
|
||||
}
|
||||
return widget->DispatchEvent(&event, status);
|
||||
nsresult rv = widget->DispatchEvent(&event, status);
|
||||
*aPreventDefault = (status == nsEventStatus_eConsumeNoDefault);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
NS_IMETHODIMP
|
||||
|
@ -47,7 +47,8 @@ protected:
|
||||
bool aIgnoreRootScrollFrame,
|
||||
float aPressure,
|
||||
unsigned short aInputSourceArg,
|
||||
bool aToWindow);
|
||||
bool aToWindow,
|
||||
bool *aPreventDefault);
|
||||
|
||||
static mozilla::widget::Modifiers GetWidgetModifiers(int32_t aModifiers);
|
||||
};
|
||||
|
@ -41,7 +41,7 @@ interface nsIDOMClientRect;
|
||||
interface nsIURI;
|
||||
interface nsIDOMEventTarget;
|
||||
|
||||
[scriptable, uuid(458f5b08-4966-4a91-8617-258afb87070e)]
|
||||
[scriptable, uuid(020deb5a-cba6-41dd-8551-72a880d01970)]
|
||||
interface nsIDOMWindowUtils : nsISupports {
|
||||
|
||||
/**
|
||||
@ -213,7 +213,8 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
const long MODIFIER_OS = 0x0400;
|
||||
|
||||
/** Synthesize a mouse event. The event types supported are:
|
||||
* mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu
|
||||
* mousedown, mouseup, mousemove, mouseover, mouseout, contextmenu,
|
||||
* MozMouseHitTest
|
||||
*
|
||||
* Events are sent in coordinates offset by aX and aY from the window.
|
||||
*
|
||||
@ -244,16 +245,18 @@ interface nsIDOMWindowUtils : nsISupports {
|
||||
* @param aPressure touch input pressure: 0.0 -> 1.0
|
||||
* @param aInputSourceArg input source, see nsIDOMMouseEvent for values,
|
||||
* defaults to mouse input.
|
||||
*
|
||||
* returns true if the page called prevent default on this event
|
||||
*/
|
||||
void sendMouseEvent(in AString aType,
|
||||
in float aX,
|
||||
in float aY,
|
||||
in long aButton,
|
||||
in long aClickCount,
|
||||
in long aModifiers,
|
||||
[optional] in boolean aIgnoreRootScrollFrame,
|
||||
[optional] in float aPressure,
|
||||
[optional] in unsigned short aInputSourceArg);
|
||||
boolean sendMouseEvent(in AString aType,
|
||||
in float aX,
|
||||
in float aY,
|
||||
in long aButton,
|
||||
in long aClickCount,
|
||||
in long aModifiers,
|
||||
[optional] in boolean aIgnoreRootScrollFrame,
|
||||
[optional] in float aPressure,
|
||||
[optional] in unsigned short aInputSourceArg);
|
||||
|
||||
/** Synthesize a touch event. The event types supported are:
|
||||
* touchstart, touchend, touchmove, and touchcancel
|
||||
|
@ -1345,8 +1345,9 @@ TabChild::RecvMouseEvent(const nsString& aType,
|
||||
{
|
||||
nsCOMPtr<nsIDOMWindowUtils> utils(GetDOMWindowUtils());
|
||||
NS_ENSURE_TRUE(utils, true);
|
||||
bool ignored = false;
|
||||
utils->SendMouseEvent(aType, aX, aY, aButton, aClickCount, aModifiers,
|
||||
aIgnoreRootScrollFrame, 0, 0);
|
||||
aIgnoreRootScrollFrame, 0, 0, &ignored);
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -206,11 +206,13 @@ function _parseModifiers(aEvent)
|
||||
* a mousedown followed by a mouse up is performed.
|
||||
*
|
||||
* aWindow is optional, and defaults to the current window object.
|
||||
*
|
||||
* Returns whether the event had preventDefault() called on it.
|
||||
*/
|
||||
function synthesizeMouse(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
|
||||
{
|
||||
var rect = aTarget.getBoundingClientRect();
|
||||
synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
|
||||
return synthesizeMouseAtPoint(rect.left + aOffsetX, rect.top + aOffsetY,
|
||||
aEvent, aWindow);
|
||||
}
|
||||
function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
|
||||
@ -234,6 +236,7 @@ function synthesizeTouch(aTarget, aOffsetX, aOffsetY, aEvent, aWindow)
|
||||
function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
|
||||
{
|
||||
var utils = _getDOMWindowUtils(aWindow);
|
||||
var defaultPrevented = false;
|
||||
|
||||
if (utils) {
|
||||
var button = aEvent.button || 0;
|
||||
@ -241,12 +244,17 @@ function synthesizeMouseAtPoint(left, top, aEvent, aWindow)
|
||||
var modifiers = _parseModifiers(aEvent);
|
||||
var pressure = ("pressure" in aEvent) ? aEvent.pressure : 0;
|
||||
var inputSource = ("inputSource" in aEvent) ? aEvent.inputSource : 0;
|
||||
var types = (("type" in aEvent) && aEvent.type) ? [aEvent.type] : ["mousedown", "mouseup"];
|
||||
|
||||
types.forEach(function(type) {
|
||||
utils.sendMouseEvent(type, left, top, button, clickCount, modifiers, false, pressure, inputSource);
|
||||
});
|
||||
if (("type" in aEvent) && aEvent.type) {
|
||||
defaultPrevented = utils.sendMouseEvent(type, left, top, button, clickCount, modifiers, false, pressure, inputSource);
|
||||
}
|
||||
else {
|
||||
utils.sendMouseEvent("mousedown", left, top, button, clickCount, modifiers, false, pressure, inputSource);
|
||||
utils.sendMouseEvent("mouseup", left, top, button, clickCount, modifiers, false, pressure, inputSource);
|
||||
}
|
||||
}
|
||||
|
||||
return defaultPrevented;
|
||||
}
|
||||
function synthesizeTouchAtPoint(left, top, aEvent, aWindow)
|
||||
{
|
||||
|
@ -2,12 +2,18 @@
|
||||
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
||||
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
|
||||
|
||||
#ifdef XP_WIN
|
||||
#define USE_HITTEST
|
||||
#elifdef MOZ_WIDGET_COCOA
|
||||
#define USE_HITTEST
|
||||
#endif
|
||||
|
||||
this.EXPORTED_SYMBOLS = [ "WindowDraggingElement" ];
|
||||
|
||||
this.WindowDraggingElement = function WindowDraggingElement(elem) {
|
||||
this._elem = elem;
|
||||
this._window = elem.ownerDocument.defaultView;
|
||||
#ifdef XP_WIN
|
||||
#ifdef USE_HITTEST
|
||||
if (!this.isPanel())
|
||||
this._elem.addEventListener("MozMouseHittest", this, false);
|
||||
else
|
||||
@ -54,7 +60,7 @@ WindowDraggingElement.prototype = {
|
||||
},
|
||||
handleEvent: function(aEvent) {
|
||||
let isPanel = this.isPanel();
|
||||
#ifdef XP_WIN
|
||||
#ifdef USE_HITTEST
|
||||
if (!isPanel) {
|
||||
if (this.shouldDrag(aEvent))
|
||||
aEvent.preventDefault();
|
||||
|
@ -52,7 +52,6 @@
|
||||
<statusbar id="statusbar">
|
||||
<statusbarpanel>
|
||||
<label id="statuslabel" value="Status"/>
|
||||
<label id="statuslabelnodrag" value="No Drag" onmousedown="event.preventDefault()"/>
|
||||
</statusbarpanel>
|
||||
</statusbar>
|
||||
|
||||
@ -106,26 +105,18 @@ function test_titlebar()
|
||||
var titlebar = document.getElementById("titlebar");
|
||||
var label = document.getElementById("label");
|
||||
|
||||
// on Mac, the window can also be moved with the statusbar
|
||||
// On Mac, the window can also be moved with the statusbar, but this works
|
||||
// via the MozMouseHittest event, not via mouse events.
|
||||
if (navigator.platform.indexOf("Mac") >= 0) {
|
||||
var preventDefaulted;
|
||||
|
||||
var statuslabel = document.getElementById("statuslabel");
|
||||
var statuslabelnodrag = document.getElementById("statuslabelnodrag");
|
||||
preventDefaulted = synthesizeMouse(statuslabel, 2, 2, { type: "MozMouseHittest" });
|
||||
SimpleTest.ok(preventDefaulted, "MozMouseHittest should have been defaultPrevented over statusbar");
|
||||
|
||||
origoldx = window.screenX;
|
||||
origoldy = window.screenY;
|
||||
|
||||
synthesizeMouse(statuslabel, 2, 2, { type: "mousedown" });
|
||||
synthesizeMouse(statuslabel, 22, 22, { type: "mousemove" });
|
||||
SimpleTest.is(window.screenX, origoldx + 20, "move window with statusbar horizontal");
|
||||
SimpleTest.is(window.screenY, origoldy + 20, "move window with statusbar vertical");
|
||||
synthesizeMouse(statuslabel, 22, 22, { type: "mouseup" });
|
||||
|
||||
// event was cancelled so the drag should not have occurred
|
||||
synthesizeMouse(statuslabelnodrag, 2, 2, { type: "mousedown" });
|
||||
synthesizeMouse(statuslabelnodrag, 22, 22, { type: "mousemove" });
|
||||
SimpleTest.is(window.screenX, origoldx + 20, "move window with statusbar cancelled mousedown horizontal");
|
||||
SimpleTest.is(window.screenY, origoldy + 20, "move window with statusbar cancelled mousedown vertical");
|
||||
synthesizeMouse(statuslabelnodrag, 22, 22, { type: "mouseup" });
|
||||
var button = document.getElementById("button");
|
||||
preventDefaulted = synthesizeMouse(button, 2, 2, { type: "MozMouseHittest" });
|
||||
SimpleTest.ok(!preventDefaulted, "MozMouseHittest should NOT have been defaultPrevented over button");
|
||||
}
|
||||
|
||||
origoldx = window.screenX;
|
||||
|
@ -26,12 +26,6 @@
|
||||
-moz-binding: url("chrome://global/content/bindings/general.xml#root-element");
|
||||
}
|
||||
|
||||
%ifdef XP_MACOSX
|
||||
:root[drawintitlebar="true"] {
|
||||
padding-top: 22px;
|
||||
}
|
||||
%endif
|
||||
|
||||
:root:-moz-locale-dir(rtl) {
|
||||
direction: rtl;
|
||||
}
|
||||
|
@ -184,6 +184,10 @@ typedef NSInteger NSEventGestureAxis;
|
||||
- (NSEventPhase)momentumPhase;
|
||||
@end
|
||||
|
||||
@protocol EventRedirection
|
||||
- (NSView*)targetView;
|
||||
@end
|
||||
|
||||
@interface ChildView : NSView<
|
||||
#ifdef ACCESSIBILITY
|
||||
mozAccessible,
|
||||
@ -268,6 +272,8 @@ typedef NSInteger NSEventGestureAxis;
|
||||
// class initialization
|
||||
+ (void)initialize;
|
||||
|
||||
+ (void)registerViewForDraggedTypes:(NSView*)aView;
|
||||
|
||||
// these are sent to the first responder when the window key status changes
|
||||
- (void)viewsWindowDidBecomeKey;
|
||||
- (void)viewsWindowDidResignKey;
|
||||
@ -279,6 +285,8 @@ typedef NSInteger NSEventGestureAxis;
|
||||
|
||||
- (void)handleMouseMoved:(NSEvent*)aEvent;
|
||||
|
||||
- (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent;
|
||||
|
||||
- (void)drawRect:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext;
|
||||
|
||||
- (void)drawTitlebar:(NSRect)aRect inTitlebarContext:(CGContextRef)aContext;
|
||||
|
@ -1990,6 +1990,20 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
}
|
||||
}
|
||||
|
||||
+ (void)registerViewForDraggedTypes:(NSView*)aView
|
||||
{
|
||||
[aView registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
|
||||
NSStringPboardType,
|
||||
NSHTMLPboardType,
|
||||
NSURLPboardType,
|
||||
NSFilesPromisePboardType,
|
||||
kWildcardPboardType,
|
||||
kCorePboardType_url,
|
||||
kCorePboardType_urld,
|
||||
kCorePboardType_urln,
|
||||
nil]];
|
||||
}
|
||||
|
||||
// initWithFrame:geckoChild:
|
||||
- (id)initWithFrame:(NSRect)inFrame geckoChild:(nsChildView*)inChild
|
||||
{
|
||||
@ -2037,17 +2051,8 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
}
|
||||
|
||||
// register for things we'll take from other applications
|
||||
PR_LOG(sCocoaLog, PR_LOG_ALWAYS, ("ChildView initWithFrame: registering drag types\n"));
|
||||
[self registerForDraggedTypes:[NSArray arrayWithObjects:NSFilenamesPboardType,
|
||||
NSStringPboardType,
|
||||
NSHTMLPboardType,
|
||||
NSURLPboardType,
|
||||
NSFilesPromisePboardType,
|
||||
kWildcardPboardType,
|
||||
kCorePboardType_url,
|
||||
kCorePboardType_urld,
|
||||
kCorePboardType_urln,
|
||||
nil]];
|
||||
[ChildView registerViewForDraggedTypes:self];
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self
|
||||
selector:@selector(windowBecameMain:)
|
||||
name:NSWindowDidBecomeMainNotification
|
||||
@ -2417,7 +2422,7 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
|
||||
- (BOOL)mouseDownCanMoveWindow
|
||||
{
|
||||
return NO;
|
||||
return [[self window] isMovableByWindowBackground];
|
||||
}
|
||||
|
||||
- (void)lockFocus
|
||||
@ -3388,6 +3393,25 @@ NSEvent* gLastDragMouseDownEvent = nil;
|
||||
mGeckoChild->DispatchEvent(&event, status);
|
||||
}
|
||||
|
||||
- (void)updateWindowDraggableStateOnMouseMove:(NSEvent*)theEvent
|
||||
{
|
||||
if (!theEvent || !mGeckoChild) {
|
||||
return;
|
||||
}
|
||||
|
||||
nsCocoaWindow* windowWidget = mGeckoChild->GetXULWindowWidget();
|
||||
if (!windowWidget) {
|
||||
return;
|
||||
}
|
||||
|
||||
// We assume later on that sending a hit test event won't cause widget destruction.
|
||||
nsMouseEvent hitTestEvent(true, NS_MOUSE_MOZHITTEST, mGeckoChild, nsMouseEvent::eReal);
|
||||
[self convertCocoaMouseEvent:theEvent toGeckoEvent:&hitTestEvent];
|
||||
bool result = mGeckoChild->DispatchWindowEvent(hitTestEvent);
|
||||
|
||||
[windowWidget->GetCocoaWindow() setMovableByWindowBackground:result];
|
||||
}
|
||||
|
||||
- (void)handleMouseMoved:(NSEvent*)theEvent
|
||||
{
|
||||
NS_OBJC_BEGIN_TRY_ABORT_BLOCK;
|
||||
@ -4911,6 +4935,11 @@ ChildViewMouseTracker::ViewForEvent(NSEvent* aEvent)
|
||||
|
||||
NSPoint windowEventLocation = nsCocoaUtils::EventLocationForWindow(aEvent, window);
|
||||
NSView* view = [[[window contentView] superview] hitTest:windowEventLocation];
|
||||
|
||||
while([view conformsToProtocol:@protocol(EventRedirection)]) {
|
||||
view = [(id<EventRedirection>)view targetView];
|
||||
}
|
||||
|
||||
if (![view isKindOfClass:[ChildView class]])
|
||||
return nil;
|
||||
|
||||
@ -5009,7 +5038,7 @@ ChildViewMouseTracker::WindowAcceptsEvent(NSWindow* aWindow, NSEvent* aEvent,
|
||||
NSWindow *ourWindow = [self window];
|
||||
NSView *contentView = [ourWindow contentView];
|
||||
if ([ourWindow isKindOfClass:[ToolbarWindow class]] && (self == contentView))
|
||||
return NO;
|
||||
return [ourWindow isMovableByWindowBackground];
|
||||
return [self nsChildView_NSView_mouseDownCanMoveWindow];
|
||||
}
|
||||
|
||||
|
@ -18,6 +18,7 @@
|
||||
class nsCocoaWindow;
|
||||
class nsChildView;
|
||||
class nsMenuBarX;
|
||||
@class ChildView;
|
||||
|
||||
// Value copied from BITMAP_MAX_AREA, used in nsNativeThemeCocoa.mm
|
||||
#define CUIDRAW_MAX_AREA 500000
|
||||
@ -176,6 +177,7 @@ typedef struct _nsCocoaWindowList {
|
||||
TitlebarAndBackgroundColor *mColor;
|
||||
float mUnifiedToolbarHeight;
|
||||
NSColor *mBackgroundColor;
|
||||
NSView *mTitlebarView; // strong
|
||||
}
|
||||
// Pass nil here to get the default appearance.
|
||||
- (void)setTitlebarColor:(NSColor*)aColor forActiveWindow:(BOOL)aActive;
|
||||
@ -186,6 +188,7 @@ typedef struct _nsCocoaWindowList {
|
||||
- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect sync:(BOOL)aSync;
|
||||
- (void)setTitlebarNeedsDisplayInRect:(NSRect)aRect;
|
||||
- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState;
|
||||
- (ChildView*)mainChildView;
|
||||
@end
|
||||
|
||||
class nsCocoaWindow : public nsBaseWidget, public nsPIWidgetCocoa
|
||||
|
@ -462,6 +462,10 @@ nsresult nsCocoaWindow::CreateNativeWindow(const NSRect &aRect,
|
||||
[mWindow setContentMinSize:NSMakeSize(60, 60)];
|
||||
[mWindow disableCursorRects];
|
||||
|
||||
// Make sure the window starts out not draggable by the background.
|
||||
// We will turn it on as necessary.
|
||||
[mWindow setMovableByWindowBackground:NO];
|
||||
|
||||
[[WindowDataMap sharedWindowDataMap] ensureDataForWindow:mWindow];
|
||||
mWindowMadeHere = true;
|
||||
|
||||
@ -2657,7 +2661,94 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
||||
|
||||
@end
|
||||
|
||||
// This class allows us to have a "unified toolbar" style window. It works like this:
|
||||
@interface TitlebarMouseHandlingView : NSView<EventRedirection>
|
||||
{
|
||||
ToolbarWindow* mWindow; // weak
|
||||
BOOL mProcessingRightMouseDown;
|
||||
}
|
||||
|
||||
- (id)initWithWindow:(ToolbarWindow*)aWindow;
|
||||
@end
|
||||
|
||||
@implementation TitlebarMouseHandlingView
|
||||
|
||||
- (id)initWithWindow:(ToolbarWindow*)aWindow
|
||||
{
|
||||
if ((self = [super initWithFrame:[aWindow titlebarRect]])) {
|
||||
mWindow = aWindow;
|
||||
[self setAutoresizingMask:(NSViewWidthSizable | NSViewMinYMargin)];
|
||||
[ChildView registerViewForDraggedTypes:self];
|
||||
mProcessingRightMouseDown = NO;
|
||||
}
|
||||
return self;
|
||||
}
|
||||
|
||||
- (NSView*)targetView
|
||||
{
|
||||
return [mWindow mainChildView];
|
||||
}
|
||||
|
||||
- (BOOL)mouseDownCanMoveWindow
|
||||
{
|
||||
return [mWindow isMovableByWindowBackground];
|
||||
}
|
||||
|
||||
// We redirect many types of events to the window's mainChildView simply by
|
||||
// passing the event object to the respective handler method. We don't need any
|
||||
// coordinate transformations because event coordinates are relative to the
|
||||
// window.
|
||||
// We only need to handle event types whose target NSView is determined by the
|
||||
// event's position. We don't need to handle key events and NSMouseMoved events
|
||||
// because those are only sent to the window's first responder. This view
|
||||
// doesn't override acceptsFirstResponder, so it will never receive those kinds
|
||||
// of events.
|
||||
|
||||
- (void)mouseMoved:(NSEvent*)aEvent { [[self targetView] mouseMoved:aEvent]; }
|
||||
- (void)mouseDown:(NSEvent*)aEvent { [[self targetView] mouseDown:aEvent]; }
|
||||
- (void)mouseUp:(NSEvent*)aEvent { [[self targetView] mouseUp:aEvent]; }
|
||||
- (void)mouseDragged:(NSEvent*)aEvent { [[self targetView] mouseDragged:aEvent]; }
|
||||
- (void)rightMouseDown:(NSEvent*)aEvent
|
||||
{
|
||||
// To avoid recursion...
|
||||
if (mProcessingRightMouseDown)
|
||||
return;
|
||||
mProcessingRightMouseDown = YES;
|
||||
[[self targetView] rightMouseDown:aEvent];
|
||||
mProcessingRightMouseDown = NO;
|
||||
}
|
||||
- (void)rightMouseUp:(NSEvent*)aEvent { [[self targetView] rightMouseUp:aEvent]; }
|
||||
- (void)rightMouseDragged:(NSEvent*)aEvent { [[self targetView] rightMouseDragged:aEvent]; }
|
||||
- (void)otherMouseDown:(NSEvent*)aEvent { [[self targetView] otherMouseDown:aEvent]; }
|
||||
- (void)otherMouseUp:(NSEvent*)aEvent { [[self targetView] otherMouseUp:aEvent]; }
|
||||
- (void)otherMouseDragged:(NSEvent*)aEvent { [[self targetView] otherMouseDragged:aEvent]; }
|
||||
- (void)scrollWheel:(NSEvent*)aEvent { [[self targetView] scrollWheel:aEvent]; }
|
||||
- (void)swipeWithEvent:(NSEvent*)aEvent { [[self targetView] swipeWithEvent:aEvent]; }
|
||||
- (void)beginGestureWithEvent:(NSEvent*)aEvent { [[self targetView] beginGestureWithEvent:aEvent]; }
|
||||
- (void)magnifyWithEvent:(NSEvent*)aEvent { [[self targetView] magnifyWithEvent:aEvent]; }
|
||||
- (void)rotateWithEvent:(NSEvent*)aEvent { [[self targetView] rotateWithEvent:aEvent]; }
|
||||
- (void)endGestureWithEvent:(NSEvent*)aEvent { [[self targetView] endGestureWithEvent:aEvent]; }
|
||||
- (NSDragOperation)draggingEntered:(id <NSDraggingInfo>)sender
|
||||
{ return [[self targetView] draggingEntered:sender]; }
|
||||
- (NSDragOperation)draggingUpdated:(id <NSDraggingInfo>)sender
|
||||
{ return [[self targetView] draggingUpdated:sender]; }
|
||||
- (void)draggingExited:(id <NSDraggingInfo>)sender
|
||||
{ [[self targetView] draggingExited:sender]; }
|
||||
- (BOOL)performDragOperation:(id <NSDraggingInfo>)sender
|
||||
{ return [[self targetView] performDragOperation:sender]; }
|
||||
- (void)draggedImage:(NSImage *)anImage endedAt:(NSPoint)aPoint operation:(NSDragOperation)operation
|
||||
{ [[self targetView] draggedImage:anImage endedAt:aPoint operation:operation]; }
|
||||
- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal
|
||||
{ return [[self targetView] draggingSourceOperationMaskForLocal:isLocal]; }
|
||||
- (NSArray *)namesOfPromisedFilesDroppedAtDestination:(NSURL*)dropDestination
|
||||
{ return [[self targetView] namesOfPromisedFilesDroppedAtDestination:dropDestination]; }
|
||||
- (NSMenu*)menuForEvent:(NSEvent*)aEvent
|
||||
{ return [[self targetView] menuForEvent:aEvent]; }
|
||||
|
||||
@end
|
||||
|
||||
// This class allows us to exercise control over the window's title bar. This
|
||||
// allows for a "unified toolbar" look, and for extending the content area into
|
||||
// the title bar. It works like this:
|
||||
// 1) We set the window's style to textured.
|
||||
// 2) Because of this, the background color applies to the entire window, including
|
||||
// the titlebar area. For normal textured windows, the default pattern is a
|
||||
@ -2691,6 +2782,11 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
||||
// to the containing window - the other direction doesn't work. That's why the
|
||||
// toolbar height is cached in the ToolbarWindow but nsNativeThemeCocoa can simply
|
||||
// query the window for its titlebar height when drawing the toolbar.
|
||||
@interface ToolbarWindow(Private)
|
||||
- (void)installTitlebarMouseHandlingView;
|
||||
- (void)uninstallTitlebarMouseHandlingView;
|
||||
@end;
|
||||
|
||||
@implementation ToolbarWindow
|
||||
|
||||
- (id)initWithContentRect:(NSRect)aContentRect styleMask:(NSUInteger)aStyle backing:(NSBackingStoreType)aBufferingType defer:(BOOL)aFlag
|
||||
@ -2725,6 +2821,7 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
||||
|
||||
[mColor release];
|
||||
[mBackgroundColor release];
|
||||
[mTitlebarView release];
|
||||
[super dealloc];
|
||||
|
||||
NS_OBJC_END_TRY_ABORT_BLOCK;
|
||||
@ -2804,11 +2901,48 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
||||
[self setTitlebarNeedsDisplayInRect:[self titlebarRect] sync:needSyncRedraw];
|
||||
}
|
||||
|
||||
// Extending the content area into the title bar works by redirection of both
|
||||
// drawing and mouse events.
|
||||
// The window's NSView hierarchy looks like this:
|
||||
// - border view ([[window contentView] superview])
|
||||
// - transparent title bar event redirection view
|
||||
// - window controls (traffic light buttons)
|
||||
// - content view ([window contentView], default NSView provided by the window)
|
||||
// - our main Gecko ChildView ([window mainChildView]), which has an
|
||||
// OpenGL context attached to it when accelerated
|
||||
// - possibly more ChildViews for plugins
|
||||
//
|
||||
// When the window is in title bar extension mode, the mainChildView covers the
|
||||
// whole window but is only visible in the content area of the window, because
|
||||
// it's a subview of the window's contentView and thus clipped to its dimensions.
|
||||
// This clipping is a good thing because it avoids a few problems. For example,
|
||||
// if the mainChildView weren't clipped and thus visible in the titlebar, we'd
|
||||
// have have to do the rounded corner masking and the drawing of the highlight
|
||||
// line ourselves.
|
||||
// This would be especially hard in combination with OpenGL acceleration since
|
||||
// rounded corners would require making the OpenGL context transparent, which
|
||||
// would bring another set of challenges with it. Having the window controls
|
||||
// draw on top of an OpenGL context could be hard, too.
|
||||
//
|
||||
// So title bar drawing happens in the border view. The border view's drawRect
|
||||
// method is not under our control, but we can get it to call into our code
|
||||
// using some tricks, see the TitlebarAndBackgroundColor class below.
|
||||
// Specifically, we have it call the TitlebarDrawCallback function, which
|
||||
// draws the contents of mainChildView into the provided CGContext.
|
||||
// (Even if the ChildView uses OpenGL for rendering, drawing in the title bar
|
||||
// will happen non-accelerated in that CGContext.)
|
||||
//
|
||||
// Mouse event redirection happens via a TitlebarMouseHandlingView which we
|
||||
// install below.
|
||||
- (void)setDrawsContentsIntoWindowFrame:(BOOL)aState
|
||||
{
|
||||
BOOL stateChanged = ([self drawsContentsIntoWindowFrame] != aState);
|
||||
[super setDrawsContentsIntoWindowFrame:aState];
|
||||
if (stateChanged && [[self delegate] isKindOfClass:[WindowDelegate class]]) {
|
||||
// Here we extend / shrink our mainChildView. We do that by firing a resize
|
||||
// event which will cause the ChildView to be resized to the rect returned
|
||||
// by nsCocoaWindow::GetClientBounds. GetClientBounds bases its return
|
||||
// value on what we return from drawsContentsIntoWindowFrame.
|
||||
WindowDelegate *windowDelegate = (WindowDelegate *)[self delegate];
|
||||
nsCocoaWindow *geckoWindow = [windowDelegate geckoWidget];
|
||||
if (geckoWindow) {
|
||||
@ -2823,10 +2957,35 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
||||
// we'll send a mouse move event with the correct new position.
|
||||
ChildViewMouseTracker::ResendLastMouseMoveEvent();
|
||||
|
||||
[self setTitlebarNeedsDisplayInRect:[self titlebarRect]];
|
||||
if (aState) {
|
||||
[self installTitlebarMouseHandlingView];
|
||||
} else {
|
||||
[self uninstallTitlebarMouseHandlingView];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
- (void)installTitlebarMouseHandlingView
|
||||
{
|
||||
mTitlebarView = [[TitlebarMouseHandlingView alloc] initWithWindow:self];
|
||||
[[[self contentView] superview] addSubview:mTitlebarView positioned:NSWindowBelow relativeTo:nil];
|
||||
}
|
||||
|
||||
- (void)uninstallTitlebarMouseHandlingView
|
||||
{
|
||||
[mTitlebarView removeFromSuperview];
|
||||
[mTitlebarView release];
|
||||
mTitlebarView = nil;
|
||||
}
|
||||
|
||||
- (ChildView*)mainChildView
|
||||
{
|
||||
NSView* view = [[[self contentView] subviews] lastObject];
|
||||
if (view && [view isKindOfClass:[ChildView class]])
|
||||
return (ChildView*)view;
|
||||
return nil;
|
||||
}
|
||||
|
||||
// Returning YES here makes the setShowsToolbarButton method work even though
|
||||
// the window doesn't contain an NSToolbar.
|
||||
- (BOOL)_hasToolbar
|
||||
@ -2895,6 +3054,9 @@ static const NSString* kStateShowsToolbarButton = @"showsToolbarButton";
|
||||
if (delegate && [delegate isKindOfClass:[WindowDelegate class]]) {
|
||||
nsCocoaWindow *widget = [(WindowDelegate *)delegate geckoWidget];
|
||||
if (widget) {
|
||||
if (type == NSMouseMoved) {
|
||||
[[self mainChildView] updateWindowDraggableStateOnMouseMove:anEvent];
|
||||
}
|
||||
if (gGeckoAppModalWindowList && (widget != gGeckoAppModalWindowList->window))
|
||||
return;
|
||||
if (widget->HasModalDescendents())
|
||||
@ -2960,13 +3122,13 @@ TitlebarDrawCallback(void* aInfo, CGContextRef aContext)
|
||||
NSRect titlebarRect = [window titlebarRect];
|
||||
|
||||
if ([window drawsContentsIntoWindowFrame]) {
|
||||
NSView* view = [[[window contentView] subviews] lastObject];
|
||||
if (!view || ![view isKindOfClass:[ChildView class]])
|
||||
ChildView* view = [window mainChildView];
|
||||
if (!view)
|
||||
return;
|
||||
|
||||
CGContextTranslateCTM(aContext, 0.0f, [window frame].size.height - titlebarRect.size.height);
|
||||
|
||||
[(ChildView*)view drawTitlebar:[window frame] inTitlebarContext:aContext];
|
||||
[view drawTitlebar:[window frame] inTitlebarContext:aContext];
|
||||
} else {
|
||||
BOOL isMain = [window isMainWindow];
|
||||
NSColor *titlebarColor = [window titlebarColorForActiveWindow:isMain];
|
||||
|
Loading…
Reference in New Issue
Block a user