Ver código fonte

WIP: macOS port
- Button code -> character translation now properly takes into account the conversion between bs button codes and macOS virtual key codes
- Windows now properly forward the initial (focus) mouse event
- Keys with command/control modifiers no longer attempt to be interpreted as character input
- Window close now actually closes the window, and releases it
- Opening multiple windows the same frame no longer causes all but last window to remain in the background
- Added “floating” property for windows that must remain above the main application window
- Fixed int/float GUI field drag to modify functionality so it properly handles cursor wrap on macOS
- User is now able to create directories in the file open panel
- Splash screen no longer blocks the thread while shown

Marko Pintera 7 anos atrás
pai
commit
c5d6bf5f19

+ 167 - 5
Source/BansheeCore/Private/MacOS/BsMacOSPlatform.mm

@@ -361,6 +361,150 @@ namespace bs
 
 
 namespace bs
 namespace bs
 {
 {
+	// Maps macOS keycodes to bs button codes
+	static constexpr ButtonCode KeyCodeMapping[] =
+	{
+		/*   0 */   BC_A,
+		/*   1 */   BC_S,
+		/*   2 */   BC_D,
+		/*   3 */   BC_F,
+		/*   4 */   BC_H,
+		/*   5 */   BC_G,
+		/*   6 */   BC_Z,
+		/*   7 */   BC_X,
+		/*   8 */   BC_C,
+		/*   9 */   BC_V,
+		/*  10 */   BC_GRAVE,
+		/*  11 */   BC_B,
+		/*  12 */   BC_Q,
+		/*  13 */   BC_W,
+		/*  14 */   BC_E,
+		/*  15 */   BC_R,
+		/*  16 */   BC_Y,
+		/*  17 */   BC_T,
+		/*  18 */   BC_1,
+		/*  19 */   BC_2,
+		/*  20 */   BC_3,
+		/*  21 */   BC_4,
+		/*  22 */   BC_6,
+		/*  23 */   BC_5,
+		/*  24 */   BC_EQUALS,
+		/*  25 */   BC_9,
+		/*  26 */   BC_7,
+		/*  27 */   BC_MINUS,
+		/*  28 */   BC_8,
+		/*  29 */   BC_0,
+		/*  30 */   BC_RBRACKET,
+		/*  31 */   BC_O,
+		/*  32 */   BC_U,
+		/*  33 */   BC_LBRACKET,
+		/*  34 */   BC_I,
+		/*  35 */   BC_P,
+		/*  36 */   BC_RETURN,
+		/*  37 */   BC_L,
+		/*  38 */   BC_J,
+		/*  39 */   BC_APOSTROPHE,
+		/*  40 */   BC_K,
+		/*  41 */   BC_SEMICOLON,
+		/*  42 */   BC_BACKSLASH,
+		/*  43 */   BC_COMMA,
+		/*  44 */   BC_SLASH,
+		/*  45 */   BC_N,
+		/*  46 */   BC_M,
+		/*  47 */   BC_PERIOD,
+		/*  48 */   BC_TAB,
+		/*  49 */   BC_SPACE,
+		/*  50 */   BC_GRAVE,
+		/*  51 */   BC_BACK,
+		/*  52 */   BC_NUMPADENTER,
+		/*  53 */   BC_ESCAPE,
+		/*  54 */   BC_RWIN,
+		/*  55 */   BC_LWIN,
+		/*  56 */   BC_LSHIFT,
+		/*  57 */   BC_CAPITAL,
+		/*  58 */   BC_LMENU,
+		/*  59 */   BC_LCONTROL,
+		/*  60 */   BC_RSHIFT,
+		/*  61 */   BC_RMENU,
+		/*  62 */   BC_RCONTROL,
+		/*  63 */   BC_RWIN,
+		/*  64 */   BC_UNASSIGNED,
+		/*  65 */   BC_DECIMAL,
+		/*  66 */   BC_UNASSIGNED,
+		/*  67 */   BC_MULTIPLY,
+		/*  68 */   BC_UNASSIGNED,
+		/*  69 */   BC_ADD,
+		/*  70 */   BC_UNASSIGNED,
+		/*  71 */   BC_NUMLOCK,
+		/*  72 */   BC_VOLUMEUP,
+		/*  73 */   BC_VOLUMEDOWN,
+		/*  74 */   BC_MUTE,
+		/*  75 */   BC_DIVIDE,
+		/*  76 */   BC_NUMPADENTER,
+		/*  77 */   BC_UNASSIGNED,
+		/*  78 */   BC_SUBTRACT,
+		/*  79 */   BC_UNASSIGNED,
+		/*  80 */   BC_UNASSIGNED,
+		/*  81 */   BC_NUMPADEQUALS,
+		/*  82 */   BC_NUMPAD0,
+		/*  83 */   BC_NUMPAD1,
+		/*  84 */   BC_NUMPAD2,
+		/*  85 */   BC_NUMPAD3,
+		/*  86 */   BC_NUMPAD4,
+		/*  87 */   BC_NUMPAD5,
+		/*  88 */   BC_NUMPAD6,
+		/*  89 */   BC_NUMPAD7,
+		/*  90 */   BC_UNASSIGNED,
+		/*  91 */   BC_NUMPAD8,
+		/*  92 */   BC_NUMPAD9,
+		/*  93 */   BC_CONVERT,
+		/*  94 */   BC_NOCONVERT,
+		/*  95 */   BC_NUMPADCOMMA,
+		/*  96 */   BC_F5,
+		/*  97 */   BC_F6,
+		/*  98 */   BC_F7,
+		/*  99 */   BC_F3,
+		/* 100 */   BC_F8,
+		/* 101 */   BC_F9,
+		/* 102 */   BC_UNASSIGNED,
+		/* 103 */   BC_F11,
+		/* 104 */   BC_UNASSIGNED,
+		/* 105 */   BC_UNASSIGNED,
+		/* 106 */   BC_UNASSIGNED,
+		/* 107 */   BC_SCROLL,
+		/* 108 */   BC_UNASSIGNED,
+		/* 109 */   BC_F10,
+		/* 110 */   BC_UNASSIGNED,
+		/* 111 */   BC_F12,
+		/* 112 */   BC_UNASSIGNED,
+		/* 113 */   BC_PAUSE,
+		/* 114 */   BC_INSERT,
+		/* 115 */   BC_HOME,
+		/* 116 */   BC_PGUP,
+		/* 117 */   BC_DELETE,
+		/* 118 */   BC_F4,
+		/* 119 */   BC_END,
+		/* 120 */   BC_F2,
+		/* 121 */   BC_PGDOWN,
+		/* 122 */   BC_F1,
+		/* 123 */   BC_LEFT,
+		/* 124 */   BC_RIGHT,
+		/* 125 */   BC_DOWN,
+		/* 126 */   BC_UP,
+		/* 127 */   BC_POWER
+	};
+
+	static UINT32 ButtonCodeToKeyCode[255];
+	static void initKeyCodeMapping()
+	{
+		memset(ButtonCodeToKeyCode, 0, sizeof(ButtonCodeToKeyCode));
+
+		UINT32 numKeyCodes = sizeof(KeyCodeMapping) / sizeof(KeyCodeMapping[0]);
+
+		for(UINT32 i = 0; i < numKeyCodes; i++)
+			ButtonCodeToKeyCode[KeyCodeMapping[i]] = i;
+	}
+
 	void flipY(NSScreen* screen, NSRect& rect)
 	void flipY(NSScreen* screen, NSRect& rect)
 	{
 	{
 		NSRect screenFrame = [screen frame];
 		NSRect screenFrame = [screen frame];
@@ -658,11 +802,29 @@ namespace bs
 		return mData->cachedClipboardData;
 		return mData->cachedClipboardData;
 	}
 	}
 
 
-	WString Platform::keyCodeToUnicode(UINT32 keyCode)
+	WString Platform::keyCodeToUnicode(UINT32 buttonCode)
 	{
 	{
+		UINT32 keyCode = ButtonCodeToKeyCode[buttonCode];
+
 		TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
 		TISInputSourceRef currentKeyboard = TISCopyCurrentKeyboardInputSource();
-		CFDataRef layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
-		const UCKeyboardLayout* keyLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(layoutData);
+		if(!currentKeyboard)
+			return L"";
+
+		auto layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+
+		CFRelease(currentKeyboard);
+
+		if(!layoutData)
+		{
+			currentKeyboard = TISCopyCurrentASCIICapableKeyboardInputSource();
+			layoutData = (CFDataRef)TISGetInputSourceProperty(currentKeyboard, kTISPropertyUnicodeKeyLayoutData);
+			CFRelease(currentKeyboard);
+		}
+
+		if(!layoutData)
+			return L"";
+
+		auto keyLayout = (const UCKeyboardLayout*)CFDataGetBytePtr(layoutData);
 
 
 		UINT32 keysDown = 0;
 		UINT32 keysDown = 0;
 		UniChar chars[4];
 		UniChar chars[4];
@@ -680,8 +842,6 @@ namespace bs
 			&length,
 			&length,
 			chars);
 			chars);
 
 
-		CFRelease(keyLayout);
-
 		U16String u16String((char16_t*)chars, (size_t)length);
 		U16String u16String((char16_t*)chars, (size_t)length);
 		String utf8String = UTF8::fromUTF16(u16String);
 		String utf8String = UTF8::fromUTF16(u16String);
 
 
@@ -701,6 +861,8 @@ namespace bs
 
 
 	void Platform::_startUp()
 	void Platform::_startUp()
 	{
 	{
+		initKeyCodeMapping();
+
 		mData->appDelegate = [[BSAppDelegate alloc] init];
 		mData->appDelegate = [[BSAppDelegate alloc] init];
 		mData->cursorManager = [[BSCursor alloc] initWithPlatformData:mData];
 		mData->cursorManager = [[BSCursor alloc] initWithPlatformData:mData];
 		mData->platformManager = [[BSPlatform alloc] initWithPlatformData:mData];
 		mData->platformManager = [[BSPlatform alloc] initWithPlatformData:mData];

+ 1 - 0
Source/BansheeCore/Private/MacOS/BsMacOSWindow.h

@@ -40,6 +40,7 @@ namespace bs
 		bool showDecorations = true;
 		bool showDecorations = true;
 		bool allowResize = true;
 		bool allowResize = true;
 		bool modal = false;
 		bool modal = false;
+		bool floating = false;
 		SPtr<PixelData> background;
 		SPtr<PixelData> background;
 	};
 	};
 
 

+ 30 - 11
Source/BansheeCore/Private/MacOS/BsMacOSWindow.mm

@@ -103,6 +103,12 @@ static bool keyCodeToInputCommand(uint32_t keyCode, bool shift, bs::InputCommand
 		[[event.window nextResponder] rightMouseDown:event];
 		[[event.window nextResponder] rightMouseDown:event];
 }
 }
 
 
+-(BOOL)acceptsFirstMouse:(NSEvent*)theEvent
+{
+	// Ensures that first mouse event when user hasn't yet focused the window, is received
+	return YES;
+}
+
 -(void)setBackgroundImage:(NSImage*)image
 -(void)setBackgroundImage:(NSImage*)image
 { @autoreleasepool {
 { @autoreleasepool {
 	if(image)
 	if(image)
@@ -351,19 +357,24 @@ enum class MouseEventType
 
 
 	NSUInteger modifierFlags = NSEvent.modifierFlags;
 	NSUInteger modifierFlags = NSEvent.modifierFlags;
 	bool shift = (modifierFlags & NSEventModifierFlagShift) != 0;
 	bool shift = (modifierFlags & NSEventModifierFlagShift) != 0;
+	bool control = (modifierFlags & NSEventModifierFlagControl) != 0;
+	bool command = (modifierFlags & NSEventModifierFlagCommand) != 0;
 
 
-	bs::InputCommandType ict;
-	if(keyCodeToInputCommand(keyCode, shift, ict))
-		bs::MacOSPlatform::sendInputCommandEvent(ict);
-	else
+	if(!control && !command)
 	{
 	{
-		const char* chars = [string UTF8String];
+		bs::InputCommandType ict;
+		if (keyCodeToInputCommand(keyCode, shift, ict))
+			bs::MacOSPlatform::sendInputCommandEvent(ict);
+		else
+		{
+			const char* chars = [string UTF8String];
 
 
-		bs::String utf8String(chars);
-		bs::U32String utf32String = bs::UTF8::toUTF32(utf8String);
+			bs::String utf8String(chars);
+			bs::U32String utf32String = bs::UTF8::toUTF32(utf8String);
 
 
-		for(size_t i = 0; i < utf32String.length(); i++)
-			bs::MacOSPlatform::sendCharInputEvent(utf32String[i]);
+			for (size_t i = 0; i < utf32String.length(); i++)
+				bs::MacOSPlatform::sendCharInputEvent(utf32String[i]);
+		}
 	}
 	}
 }
 }
 
 
@@ -607,7 +618,7 @@ namespace bs
 		NSString* titleString = [NSString stringWithUTF8String:desc.title.c_str()];
 		NSString* titleString = [NSString stringWithUTF8String:desc.title.c_str()];
 
 
 		[m->window setAcceptsMouseMovedEvents:YES];
 		[m->window setAcceptsMouseMovedEvents:YES];
-		[m->window setReleasedWhenClosed:NO];
+		[m->window setReleasedWhenClosed:YES];
 		[m->window setTitle:titleString];
 		[m->window setTitle:titleString];
 		[m->window makeKeyAndOrderFront:nil];
 		[m->window makeKeyAndOrderFront:nil];
 
 
@@ -632,6 +643,14 @@ namespace bs
 
 
 		m->isFullscreen = false;
 		m->isFullscreen = false;
 
 
+		// Makes sure that floating windows hide together with main app
+		// Also, for some reason it makes makeKeyAndOrderFront work properly when multiple windows are opened at the same
+		// frame. (Without it, only the last opened window moves to front)
+		[m->window setHidesOnDeactivate:YES];
+
+		if(desc.floating)
+			[m->window setLevel:NSFloatingWindowLevel];
+
 		if(desc.modal)
 		if(desc.modal)
 			m->modalSession = [NSApp beginModalSessionForWindow:m->window];
 			m->modalSession = [NSApp beginModalSessionForWindow:m->window];
 
 
@@ -802,9 +821,9 @@ namespace bs
 		if(m->isModal)
 		if(m->isModal)
 			[NSApp endModalSession:m->modalSession];
 			[NSApp endModalSession:m->modalSession];
 
 
-		// TODO - Need to unregister modal window as well
 		MacOSPlatform::unregisterWindow(this);
 		MacOSPlatform::unregisterWindow(this);
 
 
+		[m->window close];
 		m->window = nil;
 		m->window = nil;
 	}
 	}
 
 

+ 1 - 1
Source/BansheeCore/RenderAPI/BsRenderWindow.h

@@ -69,7 +69,7 @@ namespace bs
 		bool showTitleBar; /**< Determines if the title-bar should be shown or not. */
 		bool showTitleBar; /**< Determines if the title-bar should be shown or not. */
 		bool showBorder; /**< Determines if the window border should be shown or not. */
 		bool showBorder; /**< Determines if the window border should be shown or not. */
 		bool allowResize; /**< Determines if the user can resize the window by dragging on the window edges. */
 		bool allowResize; /**< Determines if the user can resize the window by dragging on the window edges. */
-		bool toolWindow; /**< Tool windows have a different look than normal windows and have no task bar entry. */
+		bool toolWindow; /**< Tool windows have no task bar entry and always remain on top of their parent window. */
 		bool modal; /**< When a modal window is open all other windows will be locked until modal window is closed. */
 		bool modal; /**< When a modal window is open all other windows will be locked until modal window is closed. */
 		bool hideUntilSwap; /**< Window will be created as hidden and only be shown when the first framebuffer swap happens. */
 		bool hideUntilSwap; /**< Window will be created as hidden and only be shown when the first framebuffer swap happens. */
 
 

+ 7 - 5
Source/BansheeEditor/GUI/BsGUIFloatField.cpp

@@ -92,19 +92,21 @@ namespace bs
 
 
 					INT32 jumpAmount = 0;
 					INT32 jumpAmount = 0;
 					Rect2I viewArea = _getParentWidget()->getTarget()->getPixelArea();
 					Rect2I viewArea = _getParentWidget()->getTarget()->getPixelArea();
-					if (event.getPosition().x < 0)
+					if (event.getPosition().x <= 0)
 					{
 					{
 						Vector2I cursorScreenPos = Cursor::instance().getScreenPosition();
 						Vector2I cursorScreenPos = Cursor::instance().getScreenPosition();
-						cursorScreenPos.x += viewArea.width;
-						jumpAmount = viewArea.width;
+
+						jumpAmount = viewArea.width - event.getPosition().x - 1;
+						cursorScreenPos.x += jumpAmount;
 
 
 						Cursor::instance().setScreenPosition(cursorScreenPos);
 						Cursor::instance().setScreenPosition(cursorScreenPos);
 					}
 					}
 					else if (event.getPosition().x >= (INT32)viewArea.width)
 					else if (event.getPosition().x >= (INT32)viewArea.width)
 					{
 					{
 						Vector2I cursorScreenPos = Cursor::instance().getScreenPosition();
 						Vector2I cursorScreenPos = Cursor::instance().getScreenPosition();
-						cursorScreenPos.x -= viewArea.width;
-						jumpAmount = -(INT32)viewArea.width;
+
+						jumpAmount = -(INT32)viewArea.width - (event.getPosition().x - (INT32)viewArea.width) + 1;
+						cursorScreenPos.x += jumpAmount;
 
 
 						Cursor::instance().setScreenPosition(cursorScreenPos);
 						Cursor::instance().setScreenPosition(cursorScreenPos);
 					}
 					}

+ 7 - 5
Source/BansheeEditor/GUI/BsGUIIntField.cpp

@@ -93,19 +93,21 @@ namespace bs
 
 
 					INT32 jumpAmount = 0;
 					INT32 jumpAmount = 0;
 					Rect2I viewArea = _getParentWidget()->getTarget()->getPixelArea();
 					Rect2I viewArea = _getParentWidget()->getTarget()->getPixelArea();
-					if (event.getPosition().x < 0)
+					if (event.getPosition().x <= 0)
 					{
 					{
 						Vector2I cursorScreenPos = Cursor::instance().getScreenPosition();
 						Vector2I cursorScreenPos = Cursor::instance().getScreenPosition();
-						cursorScreenPos.x += viewArea.width;
-						jumpAmount = viewArea.width;
+
+						jumpAmount = viewArea.width - event.getPosition().x - 1;
+						cursorScreenPos.x += jumpAmount;
 
 
 						Cursor::instance().setScreenPosition(cursorScreenPos);
 						Cursor::instance().setScreenPosition(cursorScreenPos);
 					}
 					}
 					else if (event.getPosition().x >= (INT32)viewArea.width)
 					else if (event.getPosition().x >= (INT32)viewArea.width)
 					{
 					{
 						Vector2I cursorScreenPos = Cursor::instance().getScreenPosition();
 						Vector2I cursorScreenPos = Cursor::instance().getScreenPosition();
-						cursorScreenPos.x -= viewArea.width;
-						jumpAmount = -(INT32)viewArea.width;
+
+						jumpAmount = -(INT32)viewArea.width - (event.getPosition().x - (INT32)viewArea.width) + 1;
+						cursorScreenPos.x += jumpAmount;
 
 
 						Cursor::instance().setScreenPosition(cursorScreenPos);
 						Cursor::instance().setScreenPosition(cursorScreenPos);
 					}
 					}

+ 1 - 0
Source/BansheeEditor/MacOS/BsMacOSBrowseDialogs.mm

@@ -54,6 +54,7 @@ namespace bs
 				[openDlg setCanChooseFiles:file];
 				[openDlg setCanChooseFiles:file];
 				[openDlg setCanChooseDirectories:folder];
 				[openDlg setCanChooseDirectories:folder];
 				[openDlg setAllowsMultipleSelection:multiselect];
 				[openDlg setAllowsMultipleSelection:multiselect];
+				[openDlg setCanCreateDirectories:YES];
 
 
 				NSString* pathString = [[NSString stringWithUTF8String:path.c_str()] stringByResolvingSymlinksInPath];
 				NSString* pathString = [[NSString stringWithUTF8String:path.c_str()] stringByResolvingSymlinksInPath];
 				[openDlg setDirectoryURL:[NSURL fileURLWithPath:pathString]];
 				[openDlg setDirectoryURL:[NSURL fileURLWithPath:pathString]];

+ 2 - 1
Source/BansheeEngine/Platform/BsSplashScreen.cpp

@@ -196,6 +196,7 @@ namespace bs
 		windowDesc.title = "Banshee Splash";
 		windowDesc.title = "Banshee Splash";
 		windowDesc.showDecorations = false;
 		windowDesc.showDecorations = false;
 		windowDesc.allowResize = false;
 		windowDesc.allowResize = false;
+		windowDesc.floating = true;
 
 
 		SPtr<PixelData> splashPixelData = BuiltinResources::getSplashScreen();
 		SPtr<PixelData> splashPixelData = BuiltinResources::getSplashScreen();
 		if (splashPixelData == nullptr)
 		if (splashPixelData == nullptr)
@@ -214,7 +215,7 @@ namespace bs
 
 
 		UINT64 currentTime = m->timer.getMilliseconds();
 		UINT64 currentTime = m->timer.getMilliseconds();
 		if (currentTime < SPLASH_SCREEN_DURATION_MS)
 		if (currentTime < SPLASH_SCREEN_DURATION_MS)
-			BS_THREAD_SLEEP(SPLASH_SCREEN_DURATION_MS - currentTime);
+			return;
 
 
 		bs_delete(m->window);
 		bs_delete(m->window);
 		m->window = nullptr;
 		m->window = nullptr;

+ 1 - 0
Source/BansheeGLRenderAPI/MacOS/BsMacOSRenderWindow.mm

@@ -33,6 +33,7 @@ namespace bs
 		windowDesc.showDecorations = mDesc.showTitleBar;
 		windowDesc.showDecorations = mDesc.showTitleBar;
 		windowDesc.allowResize = mDesc.allowResize;
 		windowDesc.allowResize = mDesc.allowResize;
 		windowDesc.modal = mDesc.modal;
 		windowDesc.modal = mDesc.modal;
+		windowDesc.floating = mDesc.toolWindow;
 
 
 		auto iter = mDesc.platformSpecific.find("parentWindowHandle");
 		auto iter = mDesc.platformSpecific.find("parentWindowHandle");
 		mIsChild = iter != mDesc.platformSpecific.end();
 		mIsChild = iter != mDesc.platformSpecific.end();