diff options
Diffstat (limited to 'widget/uikit/nsWindow.mm')
-rw-r--r-- | widget/uikit/nsWindow.mm | 728 |
1 files changed, 728 insertions, 0 deletions
diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm new file mode 100644 index 0000000000..ba360f928e --- /dev/null +++ b/widget/uikit/nsWindow.mm @@ -0,0 +1,728 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* This Source Code Form is subject to the terms of the Mozilla Public + * 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/. */ + +#import <UIKit/UIEvent.h> +#import <UIKit/UIGraphics.h> +#import <UIKit/UIInterface.h> +#import <UIKit/UIScreen.h> +#import <UIKit/UITapGestureRecognizer.h> +#import <UIKit/UITouch.h> +#import <UIKit/UIView.h> +#import <UIKit/UIViewController.h> +#import <UIKit/UIWindow.h> +#import <QuartzCore/QuartzCore.h> + +#include <algorithm> + +#include "nsWindow.h" +#include "nsScreenManager.h" +#include "nsAppShell.h" + +#include "nsWidgetsCID.h" +#include "nsGfxCIID.h" + +#include "gfxQuartzSurface.h" +#include "gfxUtils.h" +#include "gfxImageSurface.h" +#include "gfxContext.h" +#include "nsRegion.h" +#include "Layers.h" +#include "nsTArray.h" + +#include "mozilla/BasicEvents.h" +#include "mozilla/ProfilerLabels.h" +#include "mozilla/TouchEvents.h" +#include "mozilla/Unused.h" +#include "mozilla/dom/MouseEventBinding.h" + +using namespace mozilla; +using namespace mozilla::dom; +using namespace mozilla::layers; + +#define ALOG(args...) \ + fprintf(stderr, args); \ + fprintf(stderr, "\n") + +static LayoutDeviceIntPoint UIKitPointsToDevPixels(CGPoint aPoint, CGFloat aBackingScale) { + return LayoutDeviceIntPoint(NSToIntRound(aPoint.x * aBackingScale), + NSToIntRound(aPoint.y * aBackingScale)); +} + +static CGRect DevPixelsToUIKitPoints(const LayoutDeviceIntRect& aRect, CGFloat aBackingScale) { + return CGRectMake((CGFloat)aRect.x / aBackingScale, (CGFloat)aRect.y / aBackingScale, + (CGFloat)aRect.width / aBackingScale, (CGFloat)aRect.height / aBackingScale); +} + +// Used to retain a Cocoa object for the remainder of a method's execution. +class nsAutoRetainUIKitObject { + public: + nsAutoRetainUIKitObject(id anObject) { mObject = [anObject retain]; } + ~nsAutoRetainUIKitObject() { [mObject release]; } + + private: + id mObject; // [STRONG] +}; + +@interface ChildView : UIView { + @public + nsWindow* mGeckoChild; // weak ref + BOOL mWaitingForPaint; + CFMutableDictionaryRef mTouches; + int mNextTouchID; +} +// sets up our view, attaching it to its owning gecko view +- (id)initWithFrame:(CGRect)inFrame geckoChild:(nsWindow*)inChild; +// Our Gecko child was Destroy()ed +- (void)widgetDestroyed; +// Tear down this ChildView +- (void)delayedTearDown; +- (void)sendMouseEvent:(EventMessage)aType + point:(LayoutDeviceIntPoint)aPoint + widget:(nsWindow*)aWindow; +- (void)handleTap:(UITapGestureRecognizer*)sender; +- (BOOL)isUsingMainThreadOpenGL; +- (void)drawUsingOpenGL; +- (void)drawUsingOpenGLCallback; +- (void)sendTouchEvent:(EventMessage)aType touches:(NSSet*)aTouches widget:(nsWindow*)aWindow; +// Event handling (UIResponder) +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event; +- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event; +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event; +- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event; +@end + +@implementation ChildView ++ (Class)layerClass { + return [CAEAGLLayer class]; +} + +- (id)initWithFrame:(CGRect)inFrame geckoChild:(nsWindow*)inChild { + self.multipleTouchEnabled = YES; + if ((self = [super initWithFrame:inFrame])) { + mGeckoChild = inChild; + } + ALOG("[ChildView[%p] initWithFrame:] (mGeckoChild = %p)", (void*)self, (void*)mGeckoChild); + self.opaque = YES; + self.alpha = 1.0; + + UITapGestureRecognizer* tapRecognizer = + [[UITapGestureRecognizer alloc] initWithTarget:self action:@selector(handleTap:)]; + tapRecognizer.numberOfTapsRequired = 1; + [self addGestureRecognizer:tapRecognizer]; + + mTouches = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, nullptr, nullptr); + mNextTouchID = 0; + return self; +} + +- (void)widgetDestroyed { + mGeckoChild = nullptr; + CFRelease(mTouches); +} + +- (void)delayedTearDown { + [self removeFromSuperview]; + [self release]; +} + +- (void)sendMouseEvent:(EventMessage)aType + point:(LayoutDeviceIntPoint)aPoint + widget:(nsWindow*)aWindow { + WidgetMouseEvent event(true, aType, aWindow, WidgetMouseEvent::eReal, WidgetMouseEvent::eNormal); + + event.mRefPoint = aPoint; + event.mClickCount = 1; + event.button = MouseButton::ePrimary; + event.mTime = PR_IntervalNow(); + event.inputSource = MouseEvent_Binding::MOZ_SOURCE_UNKNOWN; + + nsEventStatus status; + aWindow->DispatchEvent(&event, status); +} + +- (void)handleTap:(UITapGestureRecognizer*)sender { + if (sender.state == UIGestureRecognizerStateEnded) { + ALOG("[ChildView[%p] handleTap]", self); + LayoutDeviceIntPoint lp = + UIKitPointsToDevPixels([sender locationInView:self], [self contentScaleFactor]); + [self sendMouseEvent:eMouseMove point:lp widget:mGeckoChild]; + [self sendMouseEvent:eMouseDown point:lp widget:mGeckoChild]; + [self sendMouseEvent:eMouseUp point:lp widget:mGeckoChild]; + } +} + +- (void)sendTouchEvent:(EventMessage)aType touches:(NSSet*)aTouches widget:(nsWindow*)aWindow { + WidgetTouchEvent event(true, aType, aWindow); + // XXX: I think nativeEvent.timestamp * 1000 is probably usable here but + // I don't care that much right now. + event.mTime = PR_IntervalNow(); + event.mTouches.SetCapacity(aTouches.count); + for (UITouch* touch in aTouches) { + LayoutDeviceIntPoint loc = + UIKitPointsToDevPixels([touch locationInView:self], [self contentScaleFactor]); + LayoutDeviceIntPoint radius = UIKitPointsToDevPixels([touch majorRadius], [touch majorRadius]); + void* value; + if (!CFDictionaryGetValueIfPresent(mTouches, touch, (const void**)&value)) { + // This shouldn't happen. + NS_ASSERTION(false, "Got a touch that we didn't know about"); + continue; + } + int id = reinterpret_cast<int>(value); + RefPtr<Touch> t = new Touch(id, loc, radius, 0.0f, 1.0f); + event.mRefPoint = loc; + event.mTouches.AppendElement(t); + } + aWindow->DispatchInputEvent(&event); +} + +- (void)touchesBegan:(NSSet*)touches withEvent:(UIEvent*)event { + ALOG("[ChildView[%p] touchesBegan", self); + if (!mGeckoChild) return; + + for (UITouch* touch : touches) { + CFDictionaryAddValue(mTouches, touch, (void*)mNextTouchID); + mNextTouchID++; + } + [self sendTouchEvent:eTouchStart touches:[event allTouches] widget:mGeckoChild]; +} + +- (void)touchesCancelled:(NSSet*)touches withEvent:(UIEvent*)event { + ALOG("[ChildView[%p] touchesCancelled", self); + [self sendTouchEvent:eTouchCancel touches:touches widget:mGeckoChild]; + for (UITouch* touch : touches) { + CFDictionaryRemoveValue(mTouches, touch); + } + if (CFDictionaryGetCount(mTouches) == 0) { + mNextTouchID = 0; + } +} + +- (void)touchesEnded:(NSSet*)touches withEvent:(UIEvent*)event { + ALOG("[ChildView[%p] touchesEnded", self); + if (!mGeckoChild) return; + + [self sendTouchEvent:eTouchEnd touches:touches widget:mGeckoChild]; + for (UITouch* touch : touches) { + CFDictionaryRemoveValue(mTouches, touch); + } + if (CFDictionaryGetCount(mTouches) == 0) { + mNextTouchID = 0; + } +} + +- (void)touchesMoved:(NSSet*)touches withEvent:(UIEvent*)event { + ALOG("[ChildView[%p] touchesMoved", self); + if (!mGeckoChild) return; + + [self sendTouchEvent:eTouchMove touches:[event allTouches] widget:mGeckoChild]; +} + +- (void)setNeedsDisplayInRect:(CGRect)aRect { + if ([self isUsingMainThreadOpenGL]) { + // Draw without calling drawRect. This prevent us from + // needing to access the normal window buffer surface unnecessarily, so we + // waste less time synchronizing the two surfaces. + if (!mWaitingForPaint) { + mWaitingForPaint = YES; + // Use NSRunLoopCommonModes instead of the default NSDefaultRunLoopMode + // so that the timer also fires while a native menu is open. + [self performSelector:@selector(drawUsingOpenGLCallback) + withObject:nil + afterDelay:0 + inModes:[NSArray arrayWithObject:NSRunLoopCommonModes]]; + } + } +} + +- (BOOL)isUsingMainThreadOpenGL { + if (!mGeckoChild || ![self window]) return NO; + + return NO; +} + +- (void)drawUsingOpenGL { + ALOG("drawUsingOpenGL"); + AUTO_PROFILER_LABEL("ChildView::drawUsingOpenGL", OTHER); + + if (!mGeckoChild->IsVisible()) return; + + mWaitingForPaint = NO; + + LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); + LayoutDeviceIntRegion region(geckoBounds); + + mGeckoChild->PaintWindow(region); +} + +// Called asynchronously after setNeedsDisplay in order to avoid entering the +// normal drawing machinery. +- (void)drawUsingOpenGLCallback { + if (mWaitingForPaint) { + [self drawUsingOpenGL]; + } +} + +// The display system has told us that a portion of our view is dirty. Tell +// gecko to paint it +- (void)drawRect:(CGRect)aRect { + CGContextRef cgContext = UIGraphicsGetCurrentContext(); + [self drawRect:aRect inContext:cgContext]; +} + +- (void)drawRect:(CGRect)aRect inContext:(CGContextRef)aContext { +#ifdef DEBUG_UPDATE + LayoutDeviceIntRect geckoBounds = mGeckoChild->GetBounds(); + + fprintf(stderr, "---- Update[%p][%p] [%f %f %f %f] cgc: %p\n gecko bounds: [%d %d %d %d]\n", + self, mGeckoChild, aRect.origin.x, aRect.origin.y, aRect.size.width, aRect.size.height, + aContext, geckoBounds.x, geckoBounds.y, geckoBounds.width, geckoBounds.height); + + CGAffineTransform xform = CGContextGetCTM(aContext); + fprintf(stderr, " xform in: [%f %f %f %f %f %f]\n", xform.a, xform.b, xform.c, xform.d, xform.tx, + xform.ty); +#endif + + if (true) { + // For Gecko-initiated repaints in OpenGL mode, drawUsingOpenGL is + // directly called from a delayed perform callback - without going through + // drawRect. + // Paints that come through here are triggered by something that Cocoa + // controls, for example by window resizing or window focus changes. + + // Do GL composition and return. + [self drawUsingOpenGL]; + return; + } + AUTO_PROFILER_LABEL("ChildView::drawRect", OTHER); + + // The CGContext that drawRect supplies us with comes with a transform that + // scales one user space unit to one Cocoa point, which can consist of + // multiple dev pixels. But Gecko expects its supplied context to be scaled + // to device pixels, so we need to reverse the scaling. + double scale = mGeckoChild->BackingScaleFactor(); + CGContextSaveGState(aContext); + CGContextScaleCTM(aContext, 1.0 / scale, 1.0 / scale); + + CGSize viewSize = [self bounds].size; + gfx::IntSize backingSize(viewSize.width * scale, viewSize.height * scale); + + CGContextSaveGState(aContext); + + LayoutDeviceIntRegion region = LayoutDeviceIntRect( + NSToIntRound(aRect.origin.x * scale), NSToIntRound(aRect.origin.y * scale), + NSToIntRound(aRect.size.width * scale), NSToIntRound(aRect.size.height * scale)); + + // Create Cairo objects. + RefPtr<gfxQuartzSurface> targetSurface; + + UniquePtrPtr<gfxContext> targetContext; + if (gfxPlatform::GetPlatform()->SupportsAzureContentForType(gfx::BackendType::CAIRO)) { + // This is dead code unless you mess with prefs, but keep it around for + // debugging. + targetSurface = new gfxQuartzSurface(aContext, backingSize); + targetSurface->SetAllowUseAsSource(false); + RefPtr<gfx::DrawTarget> dt = + gfxPlatform::CreateDrawTargetForSurface(targetSurface, backingSize); + if (!dt || !dt->IsValid()) { + gfxDevCrash(mozilla::gfx::LogReason::InvalidContext) + << "Window context problem 2 " << backingSize; + return; + } + targetContext = gfxContext::CreateOrNull(dt); + } else { + MOZ_ASSERT_UNREACHABLE("COREGRAPHICS is the only supported backend"); + } + MOZ_ASSERT(targetContext); // already checked for valid draw targets above + + // Set up the clip region. + targetContext->NewPath(); + for (auto iter = region.RectIter(); !iter.Done(); iter.Next()) { + const LayoutDeviceIntRect& r = iter.Get(); + targetContext->Rectangle(gfxRect(r.x, r.y, r.width, r.height)); + } + targetContext->Clip(); + + // nsAutoRetainCocoaObject kungFuDeathGrip(self); + bool painted = false; + targetContext = nullptr; + targetSurface = nullptr; + + CGContextRestoreGState(aContext); + + // Undo the scale transform so that from now on the context is in + // CocoaPoints again. + CGContextRestoreGState(aContext); + if (!painted && [self isOpaque]) { + // Gecko refused to draw, but we've claimed to be opaque, so we have to + // draw something--fill with white. + CGContextSetRGBFillColor(aContext, 1, 1, 1, 1); + CGContextFillRect(aContext, aRect); + } + +#ifdef DEBUG_UPDATE + fprintf(stderr, "---- update done ----\n"); + +# if 0 + CGContextSetRGBStrokeColor (aContext, + ((((unsigned long)self) & 0xff)) / 255.0, + ((((unsigned long)self) & 0xff00) >> 8) / 255.0, + ((((unsigned long)self) & 0xff0000) >> 16) / 255.0, + 0.5); +# endif + CGContextSetRGBStrokeColor(aContext, 1, 0, 0, 0.8); + CGContextSetLineWidth(aContext, 4.0); + CGContextStrokeRect(aContext, aRect); +#endif +} +@end + +nsWindow::nsWindow() + : mNativeView(nullptr), mVisible(false), mSizeMode(nsSizeMode_Normal), mParent(nullptr) {} + +nsWindow::~nsWindow() { + [mNativeView widgetDestroyed]; // Safe if mNativeView is nil. + TearDownView(); // Safe if called twice. +} + +void nsWindow::TearDownView() { + if (!mNativeView) return; + + [mNativeView performSelectorOnMainThread:@selector(delayedTearDown) + withObject:nil + waitUntilDone:false]; + mNativeView = nil; +} + +bool nsWindow::IsTopLevel() { + return mWindowType == WindowType::TopLevel || mWindowType == WindowType::Dialog || + mWindowType == WindowType::Invisible; +} + +// +// nsIWidget +// + +nsresult nsWindow::Create(nsIWidget* aParent, nsNativeWidget aNativeParent, + const LayoutDeviceIntRect& aRect, widget::InitData* aInitData) { + ALOG("nsWindow[%p]::Create %p/%p [%d %d %d %d]", (void*)this, (void*)aParent, + (void*)aNativeParent, aRect.x, aRect.y, aRect.width, aRect.height); + nsWindow* parent = (nsWindow*)aParent; + ChildView* nativeParent = (ChildView*)aNativeParent; + + if (parent == nullptr && nativeParent) parent = nativeParent->mGeckoChild; + if (parent && nativeParent == nullptr) nativeParent = parent->mNativeView; + + // for toplevel windows, bounds are fixed to full screen size + if (parent == nullptr) { + if (nsAppShell::gWindow == nil) { + mBounds = UIKitScreenManager::GetBounds(); + } else { + CGRect cgRect = [nsAppShell::gWindow bounds]; + mBounds.x = cgRect.origin.x; + mBounds.y = cgRect.origin.y; + mBounds.width = cgRect.size.width; + mBounds.height = cgRect.size.height; + } + } else { + mBounds = aRect; + } + + ALOG("nsWindow[%p]::Create bounds: %d %d %d %d", (void*)this, mBounds.x, mBounds.y, mBounds.width, + mBounds.height); + + // Set defaults which can be overriden from aInitData in BaseCreate + mWindowType = WindowType::TopLevel; + mBorderStyle = BorderStyle::Default; + + Inherited::BaseCreate(aParent, aInitData); + + NS_ASSERTION(IsTopLevel() || parent, "non top level window doesn't have a parent!"); + + mNativeView = + [[ChildView alloc] initWithFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor()) + geckoChild:this]; + mNativeView.hidden = YES; + + if (parent) { + parent->mChildren.AppendElement(this); + mParent = parent; + } + + if (nativeParent) { + [nativeParent addSubview:mNativeView]; + } else if (nsAppShell::gWindow) { + [nsAppShell::gWindow.rootViewController.view addSubview:mNativeView]; + } else { + [nsAppShell::gTopLevelViews addObject:mNativeView]; + } + + return NS_OK; +} + +void nsWindow::Destroy() { + for (uint32_t i = 0; i < mChildren.Length(); ++i) { + // why do we still have children? + mChildren[i]->SetParent(nullptr); + } + + if (mParent) mParent->mChildren.RemoveElement(this); + + [mNativeView widgetDestroyed]; + + nsBaseWidget::Destroy(); + + // ReportDestroyEvent(); + + TearDownView(); + + nsBaseWidget::OnDestroy(); + + return NS_OK; +} + +void nsWindow::Show(bool aState) { + if (aState != mVisible) { + mNativeView.hidden = aState ? NO : YES; + if (aState) { + UIView* parentView = + mParent ? mParent->mNativeView : nsAppShell::gWindow.rootViewController.view; + [parentView bringSubviewToFront:mNativeView]; + [mNativeView setNeedsDisplay]; + } + mVisible = aState; + } +} + +void nsWindow::Move(double aX, double aY) { + if (!mNativeView || (mBounds.x == aX && mBounds.y == aY)) return; + + // XXX: handle this + // The point we have is in Gecko coordinates (origin top-left). Convert + // it to Cocoa ones (origin bottom-left). + mBounds.x = aX; + mBounds.y = aY; + + mNativeView.frame = DevPixelsToUIKitPoints(mBounds, BackingScaleFactor()); + + if (mVisible) [mNativeView setNeedsDisplay]; + + ReportMoveEvent(); +} + +void nsWindow::Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) { + BOOL isMoving = (mBounds.x != aX || mBounds.y != aY); + BOOL isResizing = (mBounds.width != aWidth || mBounds.height != aHeight); + if (!mNativeView || (!isMoving && !isResizing)) return; + + if (isMoving) { + mBounds.x = aX; + mBounds.y = aY; + } + if (isResizing) { + mBounds.width = aWidth; + mBounds.height = aHeight; + } + + [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())]; + + if (mVisible && aRepaint) [mNativeView setNeedsDisplay]; + + if (isMoving) ReportMoveEvent(); + + if (isResizing) ReportSizeEvent(); +} + +void nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) { + if (!mNativeView || (mBounds.width == aWidth && mBounds.height == aHeight)) return; + + mBounds.width = aWidth; + mBounds.height = aHeight; + + [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())]; + + if (mVisible && aRepaint) [mNativeView setNeedsDisplay]; + + ReportSizeEvent(); +} + +void nsWindow::SetSizeMode(nsSizeMode aMode) { + if (aMode == static_cast<int32_t>(mSizeMode)) { + return; + } + + mSizeMode = static_cast<nsSizeMode>(aMode); + if (aMode == nsSizeMode_Maximized || aMode == nsSizeMode_Fullscreen) { + // Resize to fill screen + nsBaseWidget::InfallibleMakeFullScreen(true); + } + ReportSizeModeEvent(aMode); +} + +void nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) { + if (!mNativeView || !mVisible) return; + + MOZ_RELEASE_ASSERT(GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_WR, + "Shouldn't need to invalidate with accelerated OMTC layers!"); + + [mNativeView setNeedsLayout]; + [mNativeView setNeedsDisplayInRect:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())]; +} + +void nsWindow::SetFocus(Raise) { + [[mNativeView window] makeKeyWindow]; + [mNativeView becomeFirstResponder]; +} + +void nsWindow::WillPaintWindow() { + if (mWidgetListener) { + mWidgetListener->WillPaintWindow(this); + } +} + +bool nsWindow::PaintWindow(LayoutDeviceIntRegion aRegion) { + if (!mWidgetListener) return false; + + bool returnValue = false; + returnValue = mWidgetListener->PaintWindow(this, aRegion); + + if (mWidgetListener) { + mWidgetListener->DidPaintWindow(); + } + + return returnValue; +} + +void nsWindow::ReportMoveEvent() { NotifyWindowMoved(mBounds.x, mBounds.y); } + +void nsWindow::ReportSizeModeEvent(nsSizeMode aMode) { + if (mWidgetListener) { + // This is terrible. + nsSizeMode theMode; + switch (aMode) { + case nsSizeMode_Maximized: + theMode = nsSizeMode_Maximized; + break; + case nsSizeMode_Fullscreen: + theMode = nsSizeMode_Fullscreen; + break; + default: + return; + } + mWidgetListener->SizeModeChanged(theMode); + } +} + +void nsWindow::ReportSizeEvent() { + if (mWidgetListener) { + LayoutDeviceIntRect innerBounds = GetClientBounds(); + mWidgetListener->WindowResized(this, innerBounds.width, innerBounds.height); + } +} + +LayoutDeviceIntRect nsWindow::GetScreenBounds() { + return LayoutDeviceIntRect(WidgetToScreenOffset(), mBounds.Size()); +} + +LayoutDeviceIntPoint nsWindow::WidgetToScreenOffset() { + LayoutDeviceIntPoint offset(0, 0); + if (mParent) { + offset = mParent->WidgetToScreenOffset(); + } + + CGPoint temp = [mNativeView convertPoint:temp toView:nil]; + + if (!mParent && nsAppShell::gWindow) { + // convert to screen coords + temp = [nsAppShell::gWindow convertPoint:temp toWindow:nil]; + } + + offset.x += temp.x; + offset.y += temp.y; + + return offset; +} + +nsresult nsWindow::DispatchEvent(mozilla::WidgetGUIEvent* aEvent, nsEventStatus& aStatus) { + aStatus = nsEventStatus_eIgnore; + nsCOMPtr<nsIWidget> kungFuDeathGrip(aEvent->mWidget); + + if (mWidgetListener) aStatus = mWidgetListener->HandleEvent(aEvent, mUseAttachedEvents); + + return NS_OK; +} + +void nsWindow::SetInputContext(const InputContext& aContext, const InputContextAction& aAction) { + // TODO: actually show VKB + mInputContext = aContext; +} + +mozilla::widget::InputContext nsWindow::GetInputContext() { return mInputContext; } + +void nsWindow::SetBackgroundColor(const nscolor& aColor) { + mNativeView.backgroundColor = [UIColor colorWithRed:NS_GET_R(aColor) + green:NS_GET_G(aColor) + blue:NS_GET_B(aColor) + alpha:NS_GET_A(aColor)]; +} + +void* nsWindow::GetNativeData(uint32_t aDataType) { + void* retVal = nullptr; + + switch (aDataType) { + case NS_NATIVE_WIDGET: + retVal = (void*)mNativeView; + break; + + case NS_NATIVE_WINDOW: + retVal = [mNativeView window]; + break; + + case NS_NATIVE_GRAPHIC: + NS_ERROR("Requesting NS_NATIVE_GRAPHIC on a UIKit child view!"); + break; + + case NS_NATIVE_OFFSETX: + retVal = 0; + break; + + case NS_NATIVE_OFFSETY: + retVal = 0; + break; + + case NS_RAW_NATIVE_IME_CONTEXT: + retVal = GetPseudoIMEContext(); + if (retVal) { + break; + } + retVal = NS_ONLY_ONE_NATIVE_IME_CONTEXT; + break; + } + + return retVal; +} + +CGFloat nsWindow::BackingScaleFactor() { + if (mNativeView) { + return [mNativeView contentScaleFactor]; + } + return [UIScreen mainScreen].scale; +} + +int32_t nsWindow::RoundsWidgetCoordinatesTo() { + if (BackingScaleFactor() == 2.0) { + return 2; + } + return 1; +} + +already_AddRefed<nsIWidget> nsIWidget::CreateTopLevelWindow() { + nsCOMPtr<nsIWidget> window = new nsWindow(); + return window.forget(); +} + +already_AddRefed<nsIWidget> nsIWidget::CreateChildWindow() { + nsCOMPtr<nsIWidget> window = new nsWindow(); + return window.forget(); +} |