diff options
Diffstat (limited to 'widget/uikit')
-rw-r--r-- | widget/uikit/GfxInfo.cpp | 218 | ||||
-rw-r--r-- | widget/uikit/GfxInfo.h | 78 | ||||
-rw-r--r-- | widget/uikit/moz.build | 19 | ||||
-rw-r--r-- | widget/uikit/nsAppShell.h | 57 | ||||
-rw-r--r-- | widget/uikit/nsAppShell.mm | 271 | ||||
-rw-r--r-- | widget/uikit/nsLookAndFeel.h | 35 | ||||
-rw-r--r-- | widget/uikit/nsLookAndFeel.mm | 401 | ||||
-rw-r--r-- | widget/uikit/nsScreenManager.h | 60 | ||||
-rw-r--r-- | widget/uikit/nsScreenManager.mm | 146 | ||||
-rw-r--r-- | widget/uikit/nsWidgetFactory.mm | 71 | ||||
-rw-r--r-- | widget/uikit/nsWindow.h | 125 | ||||
-rw-r--r-- | widget/uikit/nsWindow.mm | 862 |
12 files changed, 2343 insertions, 0 deletions
diff --git a/widget/uikit/GfxInfo.cpp b/widget/uikit/GfxInfo.cpp new file mode 100644 index 000000000..2aea3b5ea --- /dev/null +++ b/widget/uikit/GfxInfo.cpp @@ -0,0 +1,218 @@ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#include "GfxInfo.h" +#include "nsServiceManagerUtils.h" + +namespace mozilla { +namespace widget { + + +#ifdef DEBUG +NS_IMPL_ISUPPORTS_INHERITED(GfxInfo, GfxInfoBase, nsIGfxInfoDebug) +#endif + +GfxInfo::GfxInfo() +{ +} + +GfxInfo::~GfxInfo() +{ +} + +nsresult +GfxInfo::GetD2DEnabled(bool *aEnabled) +{ + return NS_ERROR_FAILURE; +} + +nsresult +GfxInfo::GetDWriteEnabled(bool *aEnabled) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetDWriteVersion(nsAString & aDwriteVersion) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetCleartypeParameters(nsAString & aCleartypeParams) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDescription(nsAString & aAdapterDescription) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDescription2(nsAString & aAdapterDescription) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterRAM(nsAString & aAdapterRAM) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterRAM2(nsAString & aAdapterRAM) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriver(nsAString & aAdapterDriver) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriver2(nsAString & aAdapterDriver) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVersion(nsAString & aAdapterDriverVersion) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriverDate(nsAString & aAdapterDriverDate) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDriverDate2(nsAString & aAdapterDriverDate) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterVendorID(nsAString & aAdapterVendorID) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterVendorID2(nsAString & aAdapterVendorID) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDeviceID(nsAString & aAdapterDeviceID) +{ + return NS_ERROR_FAILURE; + return NS_OK; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterDeviceID2(nsAString & aAdapterDeviceID) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterSubsysID(nsAString & aAdapterSubsysID) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetAdapterSubsysID2(nsAString & aAdapterSubsysID) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP +GfxInfo::GetIsGPU2Active(bool* aIsGPU2Active) +{ + return NS_ERROR_FAILURE; +} + +const nsTArray<GfxDriverInfo>& +GfxInfo::GetGfxDriverInfo() +{ + if (mDriverInfo->IsEmpty()) { + APPEND_TO_DRIVER_BLOCKLIST2(OperatingSystem::Ios, + (nsAString&) GfxDriverInfo::GetDeviceVendor(VendorAll), GfxDriverInfo::allDevices, + nsIGfxInfo::FEATURE_OPENGL_LAYERS, nsIGfxInfo::FEATURE_STATUS_OK, + DRIVER_COMPARISON_IGNORED, GfxDriverInfo::allDriverVersions ); + } + + return *mDriverInfo; +} + +nsresult +GfxInfo::GetFeatureStatusImpl(int32_t aFeature, + int32_t *aStatus, + nsAString & aSuggestedDriverVersion, + const nsTArray<GfxDriverInfo>& aDriverInfo, + OperatingSystem* aOS /* = nullptr */) +{ + NS_ENSURE_ARG_POINTER(aStatus); + aSuggestedDriverVersion.SetIsVoid(true); + *aStatus = nsIGfxInfo::FEATURE_STATUS_UNKNOWN; + if (aOS) + *aOS = OperatingSystem::Ios; + + // OpenGL layers are never blacklisted on iOS. + // This early return is so we avoid potentially slow + // GLStrings initialization on startup when we initialize GL layers. + if (aFeature == nsIGfxInfo::FEATURE_OPENGL_LAYERS || + aFeature == nsIGfxInfo::FEATURE_WEBGL_OPENGL || + aFeature == nsIGfxInfo::FEATURE_WEBGL_MSAA) { + *aStatus = nsIGfxInfo::FEATURE_STATUS_OK; + return NS_OK; + } + + return GfxInfoBase::GetFeatureStatusImpl(aFeature, aStatus, aSuggestedDriverVersion, aDriverInfo, aOS); +} + +#ifdef DEBUG + +// Implement nsIGfxInfoDebug + +NS_IMETHODIMP GfxInfo::SpoofVendorID(const nsAString & aVendorID) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP GfxInfo::SpoofDeviceID(const nsAString & aDeviceID) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP GfxInfo::SpoofDriverVersion(const nsAString & aDriverVersion) +{ + return NS_ERROR_FAILURE; +} + +NS_IMETHODIMP GfxInfo::SpoofOSVersion(uint32_t aVersion) +{ + return NS_ERROR_FAILURE; +} + +#endif + +} +} diff --git a/widget/uikit/GfxInfo.h b/widget/uikit/GfxInfo.h new file mode 100644 index 000000000..16a224251 --- /dev/null +++ b/widget/uikit/GfxInfo.h @@ -0,0 +1,78 @@ +/* vim: se cin sw=2 ts=2 et : */ +/* -*- Mode: C++; tab-width: 2; 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/. */ + +#ifndef __mozilla_widget_GfxInfo_h__ +#define __mozilla_widget_GfxInfo_h__ + +#include "GfxInfoBase.h" +#include "GfxDriverInfo.h" + +#include "nsString.h" +#include "mozilla/UniquePtr.h" + +namespace mozilla { + +namespace gl { +class GLContext; +} + +namespace widget { + +class GfxInfo : public GfxInfoBase +{ +private: + ~GfxInfo(); + +public: + GfxInfo(); + + // We only declare the subset of nsIGfxInfo that we actually implement. The + // rest is brought forward from GfxInfoBase. + NS_IMETHOD GetD2DEnabled(bool *aD2DEnabled); + NS_IMETHOD GetDWriteEnabled(bool *aDWriteEnabled); + NS_IMETHOD GetDWriteVersion(nsAString & aDwriteVersion); + NS_IMETHOD GetCleartypeParameters(nsAString & aCleartypeParams); + NS_IMETHOD GetAdapterDescription(nsAString & aAdapterDescription); + NS_IMETHOD GetAdapterDriver(nsAString & aAdapterDriver); + NS_IMETHOD GetAdapterVendorID(nsAString & aAdapterVendorID); + NS_IMETHOD GetAdapterDeviceID(nsAString & aAdapterDeviceID); + NS_IMETHOD GetAdapterSubsysID(nsAString & aAdapterSubsysID); + NS_IMETHOD GetAdapterRAM(nsAString & aAdapterRAM); + NS_IMETHOD GetAdapterDriverVersion(nsAString & aAdapterDriverVersion); + NS_IMETHOD GetAdapterDriverDate(nsAString & aAdapterDriverDate); + NS_IMETHOD GetAdapterDescription2(nsAString & aAdapterDescription); + NS_IMETHOD GetAdapterDriver2(nsAString & aAdapterDriver); + NS_IMETHOD GetAdapterVendorID2(nsAString & aAdapterVendorID); + NS_IMETHOD GetAdapterDeviceID2(nsAString & aAdapterDeviceID); + NS_IMETHOD GetAdapterSubsysID2(nsAString & aAdapterSubsysID); + NS_IMETHOD GetAdapterRAM2(nsAString & aAdapterRAM); + NS_IMETHOD GetAdapterDriverVersion2(nsAString & aAdapterDriverVersion); + NS_IMETHOD GetAdapterDriverDate2(nsAString & aAdapterDriverDate); + NS_IMETHOD GetIsGPU2Active(bool *aIsGPU2Active); + using GfxInfoBase::GetFeatureStatus; + using GfxInfoBase::GetFeatureSuggestedDriverVersion; + using GfxInfoBase::GetWebGLParameter; + +#ifdef DEBUG + NS_DECL_ISUPPORTS_INHERITED + NS_DECL_NSIGFXINFODEBUG +#endif + +protected: + + virtual nsresult GetFeatureStatusImpl(int32_t aFeature, + int32_t *aStatus, + nsAString & aSuggestedDriverVersion, + const nsTArray<GfxDriverInfo>& aDriverInfo, + OperatingSystem* aOS = nullptr); + virtual const nsTArray<GfxDriverInfo>& GetGfxDriverInfo(); +}; + +} // namespace widget +} // namespace mozilla + +#endif /* __mozilla_widget_GfxInfo_h__ */ diff --git a/widget/uikit/moz.build b/widget/uikit/moz.build new file mode 100644 index 000000000..50aed405b --- /dev/null +++ b/widget/uikit/moz.build @@ -0,0 +1,19 @@ +# -*- Mode: python; indent-tabs-mode: nil; tab-width: 40 -*- +# vim: set filetype=python: +# 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/. + +SOURCES += [ + 'GfxInfo.cpp', + 'nsAppShell.mm', + 'nsLookAndFeel.mm', + 'nsScreenManager.mm', + 'nsWidgetFactory.mm', + 'nsWindow.mm', +] + +FINAL_LIBRARY = 'xul' +LOCAL_INCLUDES += [ + '/widget', +] diff --git a/widget/uikit/nsAppShell.h b/widget/uikit/nsAppShell.h new file mode 100644 index 000000000..a88fa8b4f --- /dev/null +++ b/widget/uikit/nsAppShell.h @@ -0,0 +1,57 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +/* + * Runs the main native UIKit run loop, interrupting it as needed to process + * Gecko events. + */ + +#ifndef nsAppShell_h_ +#define nsAppShell_h_ + +#include "nsBaseAppShell.h" +#include "nsTArray.h" + +#include <Foundation/NSAutoreleasePool.h> +#include <CoreFoundation/CFRunLoop.h> +#include <UIKit/UIWindow.h> + +@class AppShellDelegate; + +class nsAppShell : public nsBaseAppShell +{ +public: + NS_IMETHOD ResumeNative(void); + + nsAppShell(); + + nsresult Init(); + + NS_IMETHOD Run(void); + NS_IMETHOD Exit(void); + // Called by the application delegate + void WillTerminate(void); + + static nsAppShell* gAppShell; + static UIWindow* gWindow; + static NSMutableArray* gTopLevelViews; + +protected: + virtual ~nsAppShell(); + + static void ProcessGeckoEvents(void* aInfo); + virtual void ScheduleNativeEventCallback(); + virtual bool ProcessNextNativeEvent(bool aMayWait); + + NSAutoreleasePool* mAutoreleasePool; + AppShellDelegate* mDelegate; + CFRunLoopRef mCFRunLoop; + CFRunLoopSourceRef mCFRunLoopSource; + + bool mTerminated; + bool mNotifiedWillTerminate; +}; + +#endif // nsAppShell_h_ diff --git a/widget/uikit/nsAppShell.mm b/widget/uikit/nsAppShell.mm new file mode 100644 index 000000000..ac007132f --- /dev/null +++ b/widget/uikit/nsAppShell.mm @@ -0,0 +1,271 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/UIApplication.h> +#import <UIKit/UIScreen.h> +#import <UIKit/UIWindow.h> +#import <UIKit/UIViewController.h> + +#include "nsAppShell.h" +#include "nsCOMPtr.h" +#include "nsIFile.h" +#include "nsDirectoryServiceDefs.h" +#include "nsString.h" +#include "nsIRollupListener.h" +#include "nsIWidget.h" +#include "nsThreadUtils.h" +#include "nsIWindowMediator.h" +#include "nsMemoryPressure.h" +#include "nsServiceManagerUtils.h" +#include "nsIInterfaceRequestor.h" +#include "nsIWebBrowserChrome.h" + +nsAppShell *nsAppShell::gAppShell = NULL; +UIWindow *nsAppShell::gWindow = nil; +NSMutableArray *nsAppShell::gTopLevelViews = [[NSMutableArray alloc] init]; + +#define ALOG(args...) fprintf(stderr, args); fprintf(stderr, "\n") + +// ViewController +@interface ViewController : UIViewController +@end + + +@implementation ViewController + +- (void)loadView { + ALOG("[ViewController loadView]"); + CGRect r = {{0, 0}, {100, 100}}; + self.view = [[UIView alloc] initWithFrame:r]; + [self.view setBackgroundColor:[UIColor lightGrayColor]]; + // add all of the top level views as children + for (UIView* v in nsAppShell::gTopLevelViews) { + ALOG("[ViewController.view addSubView:%p]", v); + [self.view addSubview:v]; + } + [nsAppShell::gTopLevelViews release]; + nsAppShell::gTopLevelViews = nil; +} +@end + +// AppShellDelegate +// +// Acts as a delegate for the UIApplication + +@interface AppShellDelegate : NSObject <UIApplicationDelegate> { +} +@property (strong, nonatomic) UIWindow *window; +@end + +@implementation AppShellDelegate + +- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions +{ + ALOG("[AppShellDelegate application:didFinishLaunchingWithOptions:]"); + // We only create one window, since we can only display one window at + // a time anyway. Also, iOS 4 fails to display UIWindows if you + // create them before calling UIApplicationMain, so this makes more sense. + nsAppShell::gWindow = [[[UIWindow alloc] initWithFrame:[[UIScreen mainScreen] applicationFrame]] retain]; + self.window = nsAppShell::gWindow; + + self.window.rootViewController = [[ViewController alloc] init]; + + // just to make things more visible for now + nsAppShell::gWindow.backgroundColor = [UIColor blueColor]; + [nsAppShell::gWindow makeKeyAndVisible]; + + return YES; +} + +- (void)applicationWillTerminate:(UIApplication *)application +{ + ALOG("[AppShellDelegate applicationWillTerminate:]"); + nsAppShell::gAppShell->WillTerminate(); +} + +- (void)applicationDidBecomeActive:(UIApplication *)application +{ + ALOG("[AppShellDelegate applicationDidBecomeActive:]"); +} + +- (void)applicationWillResignActive:(UIApplication *)application +{ + ALOG("[AppShellDelegate applicationWillResignActive:]"); +} + +- (void)applicationDidReceiveMemoryWarning:(UIApplication *)application +{ + ALOG("[AppShellDelegate applicationDidReceiveMemoryWarning:]"); + NS_DispatchMemoryPressure(MemPressure_New); +} +@end + +// nsAppShell implementation + +NS_IMETHODIMP +nsAppShell::ResumeNative(void) +{ + return nsBaseAppShell::ResumeNative(); +} + +nsAppShell::nsAppShell() + : mAutoreleasePool(NULL), + mDelegate(NULL), + mCFRunLoop(NULL), + mCFRunLoopSource(NULL), + mTerminated(false), + mNotifiedWillTerminate(false) +{ + gAppShell = this; +} + +nsAppShell::~nsAppShell() +{ + if (mAutoreleasePool) { + [mAutoreleasePool release]; + mAutoreleasePool = NULL; + } + + if (mCFRunLoop) { + if (mCFRunLoopSource) { + ::CFRunLoopRemoveSource(mCFRunLoop, mCFRunLoopSource, + kCFRunLoopCommonModes); + ::CFRelease(mCFRunLoopSource); + } + ::CFRelease(mCFRunLoop); + } + + gAppShell = NULL; +} + +// Init +// +// public +nsresult +nsAppShell::Init() +{ + mAutoreleasePool = [[NSAutoreleasePool alloc] init]; + + // Add a CFRunLoopSource to the main native run loop. The source is + // responsible for interrupting the run loop when Gecko events are ready. + + mCFRunLoop = [[NSRunLoop currentRunLoop] getCFRunLoop]; + NS_ENSURE_STATE(mCFRunLoop); + ::CFRetain(mCFRunLoop); + + CFRunLoopSourceContext context; + bzero(&context, sizeof(context)); + // context.version = 0; + context.info = this; + context.perform = ProcessGeckoEvents; + + mCFRunLoopSource = ::CFRunLoopSourceCreate(kCFAllocatorDefault, 0, &context); + NS_ENSURE_STATE(mCFRunLoopSource); + + ::CFRunLoopAddSource(mCFRunLoop, mCFRunLoopSource, kCFRunLoopCommonModes); + + return nsBaseAppShell::Init(); +} + +// ProcessGeckoEvents +// +// The "perform" target of mCFRunLoop, called when mCFRunLoopSource is +// signalled from ScheduleNativeEventCallback. +// +// protected static +void +nsAppShell::ProcessGeckoEvents(void* aInfo) +{ + nsAppShell* self = static_cast<nsAppShell*> (aInfo); + self->NativeEventCallback(); + self->Release(); +} + +// WillTerminate +// +// public +void +nsAppShell::WillTerminate() +{ + mNotifiedWillTerminate = true; + if (mTerminated) + return; + mTerminated = true; + // We won't get another chance to process events + NS_ProcessPendingEvents(NS_GetCurrentThread()); + + // Unless we call nsBaseAppShell::Exit() here, it might not get called + // at all. + nsBaseAppShell::Exit(); +} + +// ScheduleNativeEventCallback +// +// protected virtual +void +nsAppShell::ScheduleNativeEventCallback() +{ + if (mTerminated) + return; + + NS_ADDREF_THIS(); + + // This will invoke ProcessGeckoEvents on the main thread. + ::CFRunLoopSourceSignal(mCFRunLoopSource); + ::CFRunLoopWakeUp(mCFRunLoop); +} + +// ProcessNextNativeEvent +// +// protected virtual +bool +nsAppShell::ProcessNextNativeEvent(bool aMayWait) +{ + if (mTerminated) + return false; + + NSString* currentMode = nil; + NSDate* waitUntil = nil; + if (aMayWait) + waitUntil = [NSDate distantFuture]; + NSRunLoop* currentRunLoop = [NSRunLoop currentRunLoop]; + + BOOL eventProcessed = NO; + do { + currentMode = [currentRunLoop currentMode]; + if (!currentMode) + currentMode = NSDefaultRunLoopMode; + + if (aMayWait) + eventProcessed = [currentRunLoop runMode:currentMode beforeDate:waitUntil]; + else + [currentRunLoop acceptInputForMode:currentMode beforeDate:waitUntil]; + } while(eventProcessed && aMayWait); + + return false; +} + +// Run +// +// public +NS_IMETHODIMP +nsAppShell::Run(void) +{ + ALOG("nsAppShell::Run"); + char argv[1][4] = {"app"}; + UIApplicationMain(1, (char**)argv, nil, @"AppShellDelegate"); + // UIApplicationMain doesn't exit. :-( + return NS_OK; +} + +NS_IMETHODIMP +nsAppShell::Exit(void) +{ + if (mTerminated) + return NS_OK; + + mTerminated = true; + return nsBaseAppShell::Exit(); +} diff --git a/widget/uikit/nsLookAndFeel.h b/widget/uikit/nsLookAndFeel.h new file mode 100644 index 000000000..91c0c2d73 --- /dev/null +++ b/widget/uikit/nsLookAndFeel.h @@ -0,0 +1,35 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#ifndef __nsLookAndFeel +#define __nsLookAndFeel + +#include "nsXPLookAndFeel.h" + +class nsLookAndFeel: public nsXPLookAndFeel +{ +public: + nsLookAndFeel(); + virtual ~nsLookAndFeel(); + + virtual nsresult NativeGetColor(const ColorID aID, nscolor &aResult); + virtual nsresult GetIntImpl(IntID aID, int32_t &aResult); + virtual nsresult GetFloatImpl(FloatID aID, float &aResult); + virtual bool GetFontImpl(FontID aID, nsString& aFontName, + gfxFontStyle& aFontStyle, + float aDevPixPerCSSPixel); + virtual char16_t GetPasswordCharacterImpl() + { + // unicode value for the bullet character, used for password textfields. + return 0x2022; + } + + static bool UseOverlayScrollbars() + { + return true; + } +}; + +#endif diff --git a/widget/uikit/nsLookAndFeel.mm b/widget/uikit/nsLookAndFeel.mm new file mode 100644 index 000000000..bb593eb51 --- /dev/null +++ b/widget/uikit/nsLookAndFeel.mm @@ -0,0 +1,401 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/UIColor.h> +#import <UIKit/UIInterface.h> + +#include "nsLookAndFeel.h" +#include "nsStyleConsts.h" +#include "gfxFont.h" +#include "gfxFontConstants.h" + +nsLookAndFeel::nsLookAndFeel() + : nsXPLookAndFeel() +{ +} + +nsLookAndFeel::~nsLookAndFeel() +{ +} + +static nscolor GetColorFromUIColor(UIColor* aColor) +{ + CGColorRef cgColor = [aColor CGColor]; + CGColorSpaceModel model = CGColorSpaceGetModel(CGColorGetColorSpace(cgColor)); + const CGFloat* components = CGColorGetComponents(cgColor); + if (model == kCGColorSpaceModelRGB) { + return NS_RGB((unsigned int)(components[0] * 255.0), + (unsigned int)(components[1] * 255.0), + (unsigned int)(components[2] * 255.0)); + } + else if (model == kCGColorSpaceModelMonochrome) { + unsigned int val = (unsigned int)(components[0] * 255.0); + return NS_RGBA(val, val, val, + (unsigned int)(components[1] * 255.0)); + } + NS_NOTREACHED("Unhandled color space!"); + return 0; +} + +nsresult +nsLookAndFeel::NativeGetColor(const ColorID aID, nscolor &aResult) +{ + nsresult res = NS_OK; + + switch (aID) { + case eColorID_WindowBackground: + aResult = NS_RGB(0xff,0xff,0xff); + break; + case eColorID_WindowForeground: + aResult = NS_RGB(0x00,0x00,0x00); + break; + case eColorID_WidgetBackground: + aResult = NS_RGB(0xdd,0xdd,0xdd); + break; + case eColorID_WidgetForeground: + aResult = NS_RGB(0x00,0x00,0x00); + break; + case eColorID_WidgetSelectBackground: + aResult = NS_RGB(0x80,0x80,0x80); + break; + case eColorID_WidgetSelectForeground: + aResult = NS_RGB(0x00,0x00,0x80); + break; + case eColorID_Widget3DHighlight: + aResult = NS_RGB(0xa0,0xa0,0xa0); + break; + case eColorID_Widget3DShadow: + aResult = NS_RGB(0x40,0x40,0x40); + break; + case eColorID_TextBackground: + aResult = NS_RGB(0xff,0xff,0xff); + break; + case eColorID_TextForeground: + aResult = NS_RGB(0x00,0x00,0x00); + break; + case eColorID_TextSelectBackground: + case eColorID_highlight: // CSS2 color + aResult = NS_RGB(0xaa,0xaa,0xaa); + break; + case eColorID__moz_menuhover: + aResult = NS_RGB(0xee,0xee,0xee); + break; + case eColorID_TextSelectForeground: + case eColorID_highlighttext: // CSS2 color + case eColorID__moz_menuhovertext: + GetColor(eColorID_TextSelectBackground, aResult); + if (aResult == 0x000000) + aResult = NS_RGB(0xff,0xff,0xff); + else + aResult = NS_DONT_CHANGE_COLOR; + break; + case eColorID_IMESelectedRawTextBackground: + case eColorID_IMESelectedConvertedTextBackground: + case eColorID_IMERawInputBackground: + case eColorID_IMEConvertedTextBackground: + aResult = NS_TRANSPARENT; + break; + case eColorID_IMESelectedRawTextForeground: + case eColorID_IMESelectedConvertedTextForeground: + case eColorID_IMERawInputForeground: + case eColorID_IMEConvertedTextForeground: + aResult = NS_SAME_AS_FOREGROUND_COLOR; + break; + case eColorID_IMERawInputUnderline: + case eColorID_IMEConvertedTextUnderline: + aResult = NS_40PERCENT_FOREGROUND_COLOR; + break; + case eColorID_IMESelectedRawTextUnderline: + case eColorID_IMESelectedConvertedTextUnderline: + aResult = NS_SAME_AS_FOREGROUND_COLOR; + break; + case eColorID_SpellCheckerUnderline: + aResult = NS_RGB(0xff, 0, 0); + break; + + // + // css2 system colors http://www.w3.org/TR/REC-CSS2/ui.html#system-colors + // + case eColorID_buttontext: + case eColorID__moz_buttonhovertext: + case eColorID_captiontext: + case eColorID_menutext: + case eColorID_infotext: + case eColorID__moz_menubartext: + case eColorID_windowtext: + aResult = GetColorFromUIColor([UIColor darkTextColor]); + break; + case eColorID_activecaption: + aResult = NS_RGB(0xff,0xff,0xff); + break; + case eColorID_activeborder: + aResult = NS_RGB(0x00,0x00,0x00); + break; + case eColorID_appworkspace: + aResult = NS_RGB(0xFF,0xFF,0xFF); + break; + case eColorID_background: + aResult = NS_RGB(0x63,0x63,0xCE); + break; + case eColorID_buttonface: + case eColorID__moz_buttonhoverface: + aResult = NS_RGB(0xF0,0xF0,0xF0); + break; + case eColorID_buttonhighlight: + aResult = NS_RGB(0xFF,0xFF,0xFF); + break; + case eColorID_buttonshadow: + aResult = NS_RGB(0xDC,0xDC,0xDC); + break; + case eColorID_graytext: + aResult = NS_RGB(0x44,0x44,0x44); + break; + case eColorID_inactiveborder: + aResult = NS_RGB(0xff,0xff,0xff); + break; + case eColorID_inactivecaption: + aResult = NS_RGB(0xaa,0xaa,0xaa); + break; + case eColorID_inactivecaptiontext: + aResult = NS_RGB(0x45,0x45,0x45); + break; + case eColorID_scrollbar: + aResult = NS_RGB(0,0,0); //XXX + break; + case eColorID_threeddarkshadow: + aResult = NS_RGB(0xDC,0xDC,0xDC); + break; + case eColorID_threedshadow: + aResult = NS_RGB(0xE0,0xE0,0xE0); + break; + case eColorID_threedface: + aResult = NS_RGB(0xF0,0xF0,0xF0); + break; + case eColorID_threedhighlight: + aResult = NS_RGB(0xff,0xff,0xff); + break; + case eColorID_threedlightshadow: + aResult = NS_RGB(0xDA,0xDA,0xDA); + break; + case eColorID_menu: + aResult = NS_RGB(0xff,0xff,0xff); + break; + case eColorID_infobackground: + aResult = NS_RGB(0xFF,0xFF,0xC7); + break; + case eColorID_windowframe: + aResult = NS_RGB(0xaa,0xaa,0xaa); + break; + case eColorID_window: + case eColorID__moz_field: + case eColorID__moz_combobox: + aResult = NS_RGB(0xff,0xff,0xff); + break; + case eColorID__moz_fieldtext: + case eColorID__moz_comboboxtext: + aResult = GetColorFromUIColor([UIColor darkTextColor]); + break; + case eColorID__moz_dialog: + aResult = NS_RGB(0xaa,0xaa,0xaa); + break; + case eColorID__moz_dialogtext: + case eColorID__moz_cellhighlighttext: + case eColorID__moz_html_cellhighlighttext: + aResult = GetColorFromUIColor([UIColor darkTextColor]); + break; + case eColorID__moz_dragtargetzone: + case eColorID__moz_mac_chrome_active: + case eColorID__moz_mac_chrome_inactive: + aResult = NS_RGB(0xaa,0xaa,0xaa); + break; + case eColorID__moz_mac_focusring: + aResult = NS_RGB(0x3F,0x98,0xDD); + break; + case eColorID__moz_mac_menushadow: + aResult = NS_RGB(0xA3,0xA3,0xA3); + break; + case eColorID__moz_mac_menutextdisable: + aResult = NS_RGB(0x88,0x88,0x88); + break; + case eColorID__moz_mac_menutextselect: + aResult = NS_RGB(0xaa,0xaa,0xaa); + break; + case eColorID__moz_mac_disabledtoolbartext: + aResult = NS_RGB(0x3F,0x3F,0x3F); + break; + case eColorID__moz_mac_menuselect: + aResult = NS_RGB(0xaa,0xaa,0xaa); + break; + case eColorID__moz_buttondefault: + aResult = NS_RGB(0xDC,0xDC,0xDC); + break; + case eColorID__moz_cellhighlight: + case eColorID__moz_html_cellhighlight: + case eColorID__moz_mac_secondaryhighlight: + // For inactive list selection + aResult = NS_RGB(0xaa,0xaa,0xaa); + break; + case eColorID__moz_eventreerow: + // Background color of even list rows. + aResult = NS_RGB(0xff,0xff,0xff); + break; + case eColorID__moz_oddtreerow: + // Background color of odd list rows. + aResult = NS_TRANSPARENT; + break; + case eColorID__moz_nativehyperlinktext: + // There appears to be no available system defined color. HARDCODING to the appropriate color. + aResult = NS_RGB(0x14,0x4F,0xAE); + break; + default: + NS_WARNING("Someone asked nsILookAndFeel for a color I don't know about"); + aResult = NS_RGB(0xff,0xff,0xff); + res = NS_ERROR_FAILURE; + break; + } + + return res; +} + +NS_IMETHODIMP +nsLookAndFeel::GetIntImpl(IntID aID, int32_t &aResult) +{ + nsresult res = nsXPLookAndFeel::GetIntImpl(aID, aResult); + if (NS_SUCCEEDED(res)) + return res; + res = NS_OK; + + switch (aID) { + case eIntID_CaretBlinkTime: + aResult = 567; + break; + case eIntID_CaretWidth: + aResult = 1; + break; + case eIntID_ShowCaretDuringSelection: + aResult = 0; + break; + case eIntID_SelectTextfieldsOnKeyFocus: + // Select textfield content when focused by kbd + // used by nsEventStateManager::sTextfieldSelectModel + aResult = 1; + break; + case eIntID_SubmenuDelay: + aResult = 200; + break; + case eIntID_MenusCanOverlapOSBar: + // xul popups are not allowed to overlap the menubar. + aResult = 0; + break; + case eIntID_SkipNavigatingDisabledMenuItem: + aResult = 1; + break; + case eIntID_DragThresholdX: + case eIntID_DragThresholdY: + aResult = 4; + break; + case eIntID_ScrollArrowStyle: + aResult = eScrollArrow_None; + break; + case eIntID_ScrollSliderStyle: + aResult = eScrollThumbStyle_Proportional; + break; + case eIntID_TreeOpenDelay: + aResult = 1000; + break; + case eIntID_TreeCloseDelay: + aResult = 1000; + break; + case eIntID_TreeLazyScrollDelay: + aResult = 150; + break; + case eIntID_TreeScrollDelay: + aResult = 100; + break; + case eIntID_TreeScrollLinesMax: + aResult = 3; + break; + case eIntID_DWMCompositor: + case eIntID_WindowsClassic: + case eIntID_WindowsDefaultTheme: + case eIntID_TouchEnabled: + aResult = 0; + res = NS_ERROR_NOT_IMPLEMENTED; + break; + case eIntID_MacGraphiteTheme: + aResult = 0; + break; + case eIntID_TabFocusModel: + aResult = 1; // default to just textboxes + break; + case eIntID_ScrollToClick: + aResult = 0; + break; + case eIntID_ChosenMenuItemsShouldBlink: + aResult = 1; + break; + case eIntID_IMERawInputUnderlineStyle: + case eIntID_IMEConvertedTextUnderlineStyle: + case eIntID_IMESelectedRawTextUnderlineStyle: + case eIntID_IMESelectedConvertedTextUnderline: + aResult = NS_STYLE_TEXT_DECORATION_STYLE_SOLID; + break; + case eIntID_SpellCheckerUnderlineStyle: + aResult = NS_STYLE_TEXT_DECORATION_STYLE_DOTTED; + break; + case eIntID_ContextMenuOffsetVertical: + case eIntID_ContextMenuOffsetHorizontal: + aResult = 2; + break; + default: + aResult = 0; + res = NS_ERROR_FAILURE; + } + return res; +} + +NS_IMETHODIMP +nsLookAndFeel::GetFloatImpl(FloatID aID, float &aResult) +{ + nsresult res = nsXPLookAndFeel::GetFloatImpl(aID, aResult); + if (NS_SUCCEEDED(res)) + return res; + res = NS_OK; + + switch (aID) { + case eFloatID_IMEUnderlineRelativeSize: + aResult = 2.0f; + break; + case eFloatID_SpellCheckerUnderlineRelativeSize: + aResult = 2.0f; + break; + default: + aResult = -1.0; + res = NS_ERROR_FAILURE; + } + + return res; +} + +bool +nsLookAndFeel::GetFontImpl(FontID aID, nsString &aFontName, + gfxFontStyle &aFontStyle, + float aDevPixPerCSSPixel) +{ + // hack for now + if (aID == eFont_Window || aID == eFont_Document) { + aFontStyle.style = NS_FONT_STYLE_NORMAL; + aFontStyle.weight = NS_FONT_WEIGHT_NORMAL; + aFontStyle.stretch = NS_FONT_STRETCH_NORMAL; + aFontStyle.size = 14 * aDevPixPerCSSPixel; + aFontStyle.systemFont = true; + + aFontName.AssignLiteral("sans-serif"); + return true; + } + + //TODO: implement more here? + return false; +} diff --git a/widget/uikit/nsScreenManager.h b/widget/uikit/nsScreenManager.h new file mode 100644 index 000000000..1ff6a87ec --- /dev/null +++ b/widget/uikit/nsScreenManager.h @@ -0,0 +1,60 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#ifndef nsScreenManager_h_ +#define nsScreenManager_h_ + +#include "nsBaseScreen.h" +#include "nsIScreenManager.h" +#include "nsCOMPtr.h" +#include "nsRect.h" + +@class UIScreen; + +class UIKitScreen : public nsBaseScreen +{ +public: + explicit UIKitScreen (UIScreen* screen); + ~UIKitScreen () {} + + NS_IMETHOD GetId(uint32_t* outId) { + *outId = 0; + return NS_OK; + } + + NS_IMETHOD GetRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); + NS_IMETHOD GetAvailRect(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); + NS_IMETHOD GetRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); + NS_IMETHOD GetAvailRectDisplayPix(int32_t* aLeft, int32_t* aTop, int32_t* aWidth, int32_t* aHeight); + NS_IMETHOD GetPixelDepth(int32_t* aPixelDepth); + NS_IMETHOD GetColorDepth(int32_t* aColorDepth); + NS_IMETHOD GetContentsScaleFactor(double* aContentsScaleFactor); + NS_IMETHOD GetDefaultCSSScaleFactor(double* aScaleFactor) + { + return GetContentsScaleFactor(aScaleFactor); + } + +private: + UIScreen* mScreen; +}; + +class UIKitScreenManager : public nsIScreenManager +{ +public: + UIKitScreenManager (); + + NS_DECL_ISUPPORTS + + NS_DECL_NSISCREENMANAGER + + static LayoutDeviceIntRect GetBounds(); + +private: + virtual ~UIKitScreenManager () {} + //TODO: support >1 screen, iPad supports external displays + nsCOMPtr<nsIScreen> mScreen; +}; + +#endif // nsScreenManager_h_ diff --git a/widget/uikit/nsScreenManager.mm b/widget/uikit/nsScreenManager.mm new file mode 100644 index 000000000..601c911cd --- /dev/null +++ b/widget/uikit/nsScreenManager.mm @@ -0,0 +1,146 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/UIScreen.h> + +#include "gfxPoint.h" +#include "nsScreenManager.h" +#include "nsAppShell.h" + +static LayoutDeviceIntRect gScreenBounds; +static bool gScreenBoundsSet = false; + +UIKitScreen::UIKitScreen(UIScreen* aScreen) +{ + mScreen = [aScreen retain]; +} + +NS_IMETHODIMP +UIKitScreen::GetRect(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) +{ + return GetRectDisplayPix(outX, outY, outWidth, outHeight); +} + +NS_IMETHODIMP +UIKitScreen::GetAvailRect(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) +{ + return GetAvailRectDisplayPix(outX, outY, outWidth, outHeight); +} + +NS_IMETHODIMP +UIKitScreen::GetRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) +{ + nsIntRect rect = UIKitScreenManager::GetBounds(); + *outX = rect.x; + *outY = rect.y; + *outWidth = rect.width; + *outHeight = rect.height; + + return NS_OK; +} + +NS_IMETHODIMP +UIKitScreen::GetAvailRectDisplayPix(int32_t *outX, int32_t *outY, int32_t *outWidth, int32_t *outHeight) +{ + CGRect rect = [mScreen applicationFrame]; + CGFloat scale = [mScreen scale]; + + *outX = rect.origin.x * scale; + *outY = rect.origin.y * scale; + *outWidth = rect.size.width * scale; + *outHeight = rect.size.height * scale; + + return NS_OK; +} + +NS_IMETHODIMP +UIKitScreen::GetPixelDepth(int32_t *aPixelDepth) +{ + // Close enough. + *aPixelDepth = 24; + return NS_OK; +} + +NS_IMETHODIMP +UIKitScreen::GetColorDepth(int32_t *aColorDepth) +{ + return GetPixelDepth(aColorDepth); +} + +NS_IMETHODIMP +UIKitScreen::GetContentsScaleFactor(double* aContentsScaleFactor) +{ + *aContentsScaleFactor = [mScreen scale]; + return NS_OK; +} + +NS_IMPL_ISUPPORTS(UIKitScreenManager, nsIScreenManager) + +UIKitScreenManager::UIKitScreenManager() +: mScreen(new UIKitScreen([UIScreen mainScreen])) +{ +} + +LayoutDeviceIntRect +UIKitScreenManager::GetBounds() +{ + if (!gScreenBoundsSet) { + CGRect rect = [[UIScreen mainScreen] bounds]; + CGFloat scale = [[UIScreen mainScreen] scale]; + gScreenBounds.x = rect.origin.x * scale; + gScreenBounds.y = rect.origin.y * scale; + gScreenBounds.width = rect.size.width * scale; + gScreenBounds.height = rect.size.height * scale; + gScreenBoundsSet = true; + } + printf("UIKitScreenManager::GetBounds: %d %d %d %d\n", + gScreenBounds.x, gScreenBounds.y, gScreenBounds.width, gScreenBounds.height); + return gScreenBounds; +} + +NS_IMETHODIMP +UIKitScreenManager::GetPrimaryScreen(nsIScreen** outScreen) +{ + NS_IF_ADDREF(*outScreen = mScreen.get()); + return NS_OK; +} + +NS_IMETHODIMP +UIKitScreenManager::ScreenForRect(int32_t inLeft, + int32_t inTop, + int32_t inWidth, + int32_t inHeight, + nsIScreen** outScreen) +{ + return GetPrimaryScreen(outScreen); +} + +NS_IMETHODIMP +UIKitScreenManager::ScreenForId(uint32_t id, + nsIScreen** outScreen) +{ + return GetPrimaryScreen(outScreen); +} + +NS_IMETHODIMP +UIKitScreenManager::ScreenForNativeWidget(void* aWidget, nsIScreen** outScreen) +{ + return GetPrimaryScreen(outScreen); +} + +NS_IMETHODIMP +UIKitScreenManager::GetNumberOfScreens(uint32_t* aNumberOfScreens) +{ + //TODO: support multiple screens + *aNumberOfScreens = 1; + return NS_OK; +} + +NS_IMETHODIMP +UIKitScreenManager::GetSystemDefaultScale(float* aScale) +{ + *aScale = [UIScreen mainScreen].scale; + return NS_OK; +} diff --git a/widget/uikit/nsWidgetFactory.mm b/widget/uikit/nsWidgetFactory.mm new file mode 100644 index 000000000..9e4f028ff --- /dev/null +++ b/widget/uikit/nsWidgetFactory.mm @@ -0,0 +1,71 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#include "nsIFactory.h" +#include "nsISupports.h" +#include "nsIComponentManager.h" +#include "mozilla/ModuleUtils.h" + +#include "nsWidgetsCID.h" + +#include "nsAppShell.h" +#include "nsAppShellSingleton.h" +#include "nsLookAndFeel.h" +#include "nsScreenManager.h" +#include "nsWindow.h" + +NS_GENERIC_FACTORY_CONSTRUCTOR(UIKitScreenManager) +NS_GENERIC_FACTORY_CONSTRUCTOR(nsWindow) + +#include "GfxInfo.h" +namespace mozilla { +namespace widget { +// This constructor should really be shared with all platforms. +NS_GENERIC_FACTORY_CONSTRUCTOR_INIT(GfxInfo, Init) +} +} + +NS_DEFINE_NAMED_CID(NS_WINDOW_CID); +NS_DEFINE_NAMED_CID(NS_CHILD_CID); +NS_DEFINE_NAMED_CID(NS_APPSHELL_CID); +NS_DEFINE_NAMED_CID(NS_SCREENMANAGER_CID); +NS_DEFINE_NAMED_CID(NS_GFXINFO_CID); + +static const mozilla::Module::CIDEntry kWidgetCIDs[] = { + { &kNS_WINDOW_CID, false, nullptr, nsWindowConstructor }, + { &kNS_CHILD_CID, false, nullptr, nsWindowConstructor }, + { &kNS_APPSHELL_CID, false, nullptr, nsAppShellConstructor }, + { &kNS_SCREENMANAGER_CID, false, nullptr, UIKitScreenManagerConstructor }, + { &kNS_GFXINFO_CID, false, nullptr, mozilla::widget::GfxInfoConstructor }, + { nullptr } +}; + +static const mozilla::Module::ContractIDEntry kWidgetContracts[] = { + { "@mozilla.org/widgets/window/uikit;1", &kNS_WINDOW_CID }, + { "@mozilla.org/widgets/childwindow/uikit;1", &kNS_CHILD_CID }, + { "@mozilla.org/widget/appshell/uikit;1", &kNS_APPSHELL_CID }, + { "@mozilla.org/gfx/screenmanager;1", &kNS_SCREENMANAGER_CID }, + { "@mozilla.org/gfx/info;1", &kNS_GFXINFO_CID }, + { nullptr } +}; + +static void +nsWidgetUIKitModuleDtor() +{ + nsLookAndFeel::Shutdown(); + nsAppShellShutdown(); +} + +static const mozilla::Module kWidgetModule = { + mozilla::Module::kVersion, + kWidgetCIDs, + kWidgetContracts, + nullptr, + nullptr, + nsAppShellInit, + nsWidgetUIKitModuleDtor +}; + +NSMODULE_DEFN(nsWidgetUIKitModule) = &kWidgetModule; diff --git a/widget/uikit/nsWindow.h b/widget/uikit/nsWindow.h new file mode 100644 index 000000000..cb18c0906 --- /dev/null +++ b/widget/uikit/nsWindow.h @@ -0,0 +1,125 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/. */ + +#ifndef NSWINDOW_H_ +#define NSWINDOW_H_ + +#include "nsBaseWidget.h" +#include "gfxPoint.h" + +#include "nsTArray.h" + +@class UIWindow; +@class UIView; +@class ChildView; + +class nsWindow : + public nsBaseWidget +{ + typedef nsBaseWidget Inherited; + +public: + nsWindow(); + + NS_DECL_ISUPPORTS_INHERITED + + // + // nsIWidget + // + + virtual MOZ_MUST_USE nsresult Create(nsIWidget* aParent, + nsNativeWidget aNativeParent, + const LayoutDeviceIntRect& aRect, + nsWidgetInitData* aInitData = nullptr) + override; + virtual void Destroy() override; + NS_IMETHOD Show(bool aState) override; + NS_IMETHOD Enable(bool aState) override { + return NS_OK; + } + virtual bool IsEnabled() const override { + return true; + } + virtual bool IsVisible() const override { + return mVisible; + } + NS_IMETHOD SetFocus(bool aState=false) override; + virtual LayoutDeviceIntPoint WidgetToScreenOffset() override; + + virtual void SetBackgroundColor(const nscolor &aColor) override; + virtual void* GetNativeData(uint32_t aDataType) override; + + NS_IMETHOD Move(double aX, double aY) override; + virtual void SetSizeMode(nsSizeMode aMode) override; + void EnteredFullScreen(bool aFullScreen); + NS_IMETHOD Resize(double aWidth, double aHeight, bool aRepaint) override; + NS_IMETHOD Resize(double aX, double aY, double aWidth, double aHeight, bool aRepaint) override; + virtual LayoutDeviceIntRect GetScreenBounds() override; + void ReportMoveEvent(); + void ReportSizeEvent(); + void ReportSizeModeEvent(nsSizeMode aMode); + + CGFloat BackingScaleFactor(); + void BackingScaleFactorChanged(); + virtual float GetDPI() override { + //XXX: terrible + return 326.0f; + } + virtual double GetDefaultScaleInternal() override { + return BackingScaleFactor(); + } + virtual int32_t RoundsWidgetCoordinatesTo() override; + + NS_IMETHOD SetTitle(const nsAString& aTitle) override { + return NS_OK; + } + + NS_IMETHOD Invalidate(const LayoutDeviceIntRect& aRect) override; + virtual nsresult ConfigureChildren(const nsTArray<Configuration>& aConfigurations) override; + NS_IMETHOD DispatchEvent(mozilla::WidgetGUIEvent* aEvent, + nsEventStatus& aStatus) override; + + void WillPaintWindow(); + bool PaintWindow(LayoutDeviceIntRegion aRegion); + + bool HasModalDescendents() { return false; } + + //NS_IMETHOD NotifyIME(const IMENotification& aIMENotification) override; + NS_IMETHOD_(void) SetInputContext( + const InputContext& aContext, + const InputContextAction& aAction); + NS_IMETHOD_(InputContext) GetInputContext(); + /* + NS_IMETHOD_(bool) ExecuteNativeKeyBinding( + NativeKeyBindingsType aType, + const mozilla::WidgetKeyboardEvent& aEvent, + DoCommandCallback aCallback, + void* aCallbackData) override; + */ + +protected: + virtual ~nsWindow(); + void BringToFront(); + nsWindow *FindTopLevel(); + bool IsTopLevel(); + nsresult GetCurrentOffset(uint32_t &aOffset, uint32_t &aLength); + nsresult DeleteRange(int aOffset, int aLen); + + void TearDownView(); + + ChildView* mNativeView; + bool mVisible; + nsTArray<nsWindow*> mChildren; + nsWindow* mParent; + InputContext mInputContext; + + void OnSizeChanged(const mozilla::gfx::IntSize& aSize); + + static void DumpWindows(); + static void DumpWindows(const nsTArray<nsWindow*>& wins, int indent = 0); + static void LogWindow(nsWindow *win, int index, int indent); +}; + +#endif /* NSWINDOW_H_ */ diff --git a/widget/uikit/nsWindow.mm b/widget/uikit/nsWindow.mm new file mode 100644 index 000000000..874626237 --- /dev/null +++ b/widget/uikit/nsWindow.mm @@ -0,0 +1,862 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* 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/TouchEvents.h" +#include "mozilla/Unused.h" + +#include "GeckoProfiler.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 = WidgetMouseEvent::eLeftButton; + event.mTime = PR_IntervalNow(); + event.inputSource = nsIDOMMouseEvent::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 mGeckoChild->GetLayerManager(nullptr)->GetBackendType() == mozilla::layers::LayersBackend::LAYERS_OPENGL; +} + +- (void)drawUsingOpenGL +{ + ALOG("drawUsingOpenGL"); + PROFILER_LABEL("ChildView", "drawUsingOpenGL", + js::ProfileEntry::Category::GRAPHICS); + + 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; + } + PROFILER_LABEL("ChildView", "drawRect", + js::ProfileEntry::Category::GRAPHICS); + + // 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; + + RefPtr<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::GetPlatform()->CreateDrawTargetForSurface(targetSurface, + backingSize); + if (!dt || !dt->IsValid()) { + gfxDevCrash(mozilla::gfx::LogReason::InvalidContext) << "Window context problem 2 " << backingSize; + return; + } + dt->AddUserData(&gfxContext::sDontUseAsSourceKey, dt, nullptr); + 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; + if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_BASIC) { + nsBaseWidget::AutoLayerManagerSetup + setupLayerManager(mGeckoChild, targetContext, BufferMode::BUFFER_NONE); + painted = mGeckoChild->PaintWindow(region); + } else if (mGeckoChild->GetLayerManager()->GetBackendType() == LayersBackend::LAYERS_CLIENT) { + // We only need this so that we actually get DidPaintWindow fired + painted = mGeckoChild->PaintWindow(region); + } + + 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 + +NS_IMPL_ISUPPORTS_INHERITED0(nsWindow, Inherited) + +nsWindow::nsWindow() +: mNativeView(nullptr), + mVisible(false), + 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 == eWindowType_toplevel || + mWindowType == eWindowType_dialog || + mWindowType == eWindowType_invisible; +} + +// +// nsIWidget +// + +nsresult +nsWindow::Create(nsIWidget* aParent, + nsNativeWidget aNativeParent, + const LayoutDeviceIntRect& aRect, + nsWidgetInitData* 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 = eWindowType_toplevel; + mBorderStyle = eBorderStyle_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; +} + +NS_IMETHODIMP +nsWindow::ConfigureChildren(const nsTArray<nsIWidget::Configuration>& config) +{ + for (uint32_t i = 0; i < config.Length(); ++i) { + nsWindow *childWin = (nsWindow*) config[i].mChild.get(); + childWin->Resize(config[i].mBounds.x, + config[i].mBounds.y, + config[i].mBounds.width, + config[i].mBounds.height, + false); + } + + return NS_OK; +} + +NS_IMETHODIMP +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; + } + return NS_OK; +} + +NS_IMETHODIMP +nsWindow::Move(double aX, double aY) +{ + if (!mNativeView || (mBounds.x == aX && mBounds.y == aY)) + return NS_OK; + + //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(); + return NS_OK; +} + +NS_IMETHODIMP +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 NS_OK; + + 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(); + + return NS_OK; +} + +NS_IMETHODIMP nsWindow::Resize(double aWidth, double aHeight, bool aRepaint) +{ + if (!mNativeView || (mBounds.width == aWidth && mBounds.height == aHeight)) + return NS_OK; + + mBounds.width = aWidth; + mBounds.height = aHeight; + + [mNativeView setFrame:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())]; + + if (mVisible && aRepaint) + [mNativeView setNeedsDisplay]; + + ReportSizeEvent(); + + return NS_OK; +} + +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); +} + +NS_IMETHODIMP +nsWindow::Invalidate(const LayoutDeviceIntRect& aRect) +{ + if (!mNativeView || !mVisible) + return NS_OK; + + MOZ_RELEASE_ASSERT(GetLayerManager()->GetBackendType() != LayersBackend::LAYERS_CLIENT, + "Shouldn't need to invalidate with accelerated OMTC layers!"); + + + [mNativeView setNeedsLayout]; + [mNativeView setNeedsDisplayInRect:DevPixelsToUIKitPoints(mBounds, BackingScaleFactor())]; + + return NS_OK; +} + +NS_IMETHODIMP +nsWindow::SetFocus(bool aRaise) +{ + [[mNativeView window] makeKeyWindow]; + [mNativeView becomeFirstResponder]; + return NS_OK; +} + +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; +} + +NS_IMETHODIMP +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; +} + +NS_IMETHODIMP_(void) +nsWindow::SetInputContext(const InputContext& aContext, + const InputContextAction& aAction) +{ + //TODO: actually show VKB + mInputContext = aContext; +} + +NS_IMETHODIMP_(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: + case NS_NATIVE_DISPLAY: + 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_NATIVE_PLUGIN_PORT: + // not implemented + 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; +} |