Merge branch 'master' into jgrpp
# Conflicts: # src/gfx.cpp # src/lang/traditional_chinese.txt # src/station_cmd.cpp
This commit is contained in:
		| @@ -232,11 +232,15 @@ void VideoDriver_CocoaOpenGL::Stop() | ||||
|  | ||||
| void VideoDriver_CocoaOpenGL::PopulateSystemSprites() | ||||
| { | ||||
| 	VideoDriver_Cocoa::PopulateSystemSprites(); | ||||
|  | ||||
| 	OpenGLBackend::Get()->PopulateCursorCache(); | ||||
| } | ||||
|  | ||||
| void VideoDriver_CocoaOpenGL::ClearSystemSprites() | ||||
| { | ||||
| 	VideoDriver_Cocoa::ClearSystemSprites(); | ||||
|  | ||||
| 	CGLSetCurrentContext(this->gl_context); | ||||
| 	OpenGLBackend::Get()->ClearCursorCache(); | ||||
| } | ||||
|   | ||||
| @@ -22,7 +22,8 @@ extern bool _cocoa_video_started; | ||||
|  | ||||
| class VideoDriver_Cocoa : public VideoDriver { | ||||
| private: | ||||
| 	Dimension orig_res;   ///< Saved window size for non-fullscreen mode. | ||||
| 	Dimension orig_res;       ///< Saved window size for non-fullscreen mode. | ||||
| 	bool refresh_sys_sprites; ///< System sprites need refreshing. | ||||
|  | ||||
| public: | ||||
| 	bool setup; ///< Window is currently being created. | ||||
| @@ -45,6 +46,9 @@ public: | ||||
| 	bool ChangeResolution(int w, int h) override; | ||||
| 	bool ToggleFullscreen(bool fullscreen) override; | ||||
|  | ||||
| 	void ClearSystemSprites() override; | ||||
| 	void PopulateSystemSprites() override; | ||||
|  | ||||
| 	void EditBoxLostFocus() override; | ||||
|  | ||||
| 	std::vector<int> GetListOfMonitorRefreshRates() override; | ||||
|   | ||||
| @@ -98,6 +98,8 @@ VideoDriver_Cocoa::VideoDriver_Cocoa() | ||||
| 	this->setup         = false; | ||||
| 	this->buffer_locked = false; | ||||
|  | ||||
| 	this->refresh_sys_sprites = true; | ||||
|  | ||||
| 	this->window    = nil; | ||||
| 	this->cocoaview = nil; | ||||
| 	this->delegate  = nil; | ||||
| @@ -220,6 +222,19 @@ bool VideoDriver_Cocoa::ToggleFullscreen(bool full_screen) | ||||
| 	return false; | ||||
| } | ||||
|  | ||||
| void VideoDriver_Cocoa::ClearSystemSprites() | ||||
| { | ||||
| 	this->refresh_sys_sprites = true; | ||||
| } | ||||
|  | ||||
| void VideoDriver_Cocoa::PopulateSystemSprites() | ||||
| { | ||||
| 	if (this->refresh_sys_sprites && this->window != nil) { | ||||
| 		[ this->window refreshSystemSprites ]; | ||||
| 		this->refresh_sys_sprites = false; | ||||
| 	} | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Callback invoked after the blitter was changed. | ||||
|  * @return True if no error. | ||||
|   | ||||
| @@ -11,8 +11,10 @@ | ||||
| #define COCOA_WND_H | ||||
|  | ||||
| #import <Cocoa/Cocoa.h> | ||||
| #include "toolbar_gui.h" | ||||
| #include "table/sprites.h" | ||||
|  | ||||
| #ifdef MAC_OS_X_VERSION_10_12_2 | ||||
| #	define HAVE_TOUCHBAR_SUPPORT | ||||
| #endif | ||||
|  | ||||
| class VideoDriver_Cocoa; | ||||
|  | ||||
| @@ -30,70 +32,17 @@ extern NSString *OTTDMainLaunchGameEngine; | ||||
| + (NSCursor *) clearCocoaCursor; | ||||
| @end | ||||
|  | ||||
| #ifdef HAVE_OSX_1015_SDK | ||||
| /* 9 items can be displayed on the touch bar when using default buttons. */ | ||||
| static NSArray *touchBarButtonIdentifiers = @[ | ||||
| 	@"openttd.pause", | ||||
| 	@"openttd.fastforward", | ||||
| 	@"openttd.zoom_in", | ||||
| 	@"openttd.zoom_out", | ||||
| 	@"openttd.build_rail", | ||||
| 	@"openttd.build_road", | ||||
| 	@"openttd.build_tram", | ||||
| 	@"openttd.build_docks", | ||||
| 	@"openttd.build_airport", | ||||
| 	NSTouchBarItemIdentifierOtherItemsProxy | ||||
| ]; | ||||
|  | ||||
| static NSDictionary *touchBarButtonSprites = @{ | ||||
| 	@"openttd.pause":           [NSNumber numberWithInt:SPR_IMG_PAUSE], | ||||
| 	@"openttd.fastforward":     [NSNumber numberWithInt:SPR_IMG_FASTFORWARD], | ||||
| 	@"openttd.zoom_in":         [NSNumber numberWithInt:SPR_IMG_ZOOMIN], | ||||
| 	@"openttd.zoom_out":        [NSNumber numberWithInt:SPR_IMG_ZOOMOUT], | ||||
| 	@"openttd.build_rail":      [NSNumber numberWithInt:SPR_IMG_BUILDRAIL], | ||||
| 	@"openttd.build_road":      [NSNumber numberWithInt:SPR_IMG_BUILDROAD], | ||||
| 	@"openttd.build_tram":      [NSNumber numberWithInt:SPR_IMG_BUILDTRAMS], | ||||
| 	@"openttd.build_docks":     [NSNumber numberWithInt:SPR_IMG_BUILDWATER], | ||||
| 	@"openttd.build_airport":   [NSNumber numberWithInt:SPR_IMG_BUILDAIR], | ||||
| }; | ||||
|  | ||||
| static NSDictionary *touchBarButtonActions = @{ | ||||
| 	@"openttd.pause":           [NSNumber numberWithInt:MTHK_PAUSE], | ||||
| 	@"openttd.fastforward":     [NSNumber numberWithInt:MTHK_FASTFORWARD], | ||||
| 	@"openttd.zoom_in":         [NSNumber numberWithInt:MTHK_ZOOM_IN], | ||||
| 	@"openttd.zoom_out":        [NSNumber numberWithInt:MTHK_ZOOM_OUT], | ||||
| 	@"openttd.build_rail":      [NSNumber numberWithInt:MTHK_BUILD_RAIL], | ||||
| 	@"openttd.build_road":      [NSNumber numberWithInt:MTHK_BUILD_ROAD], | ||||
| 	@"openttd.build_tram":      [NSNumber numberWithInt:MTHK_BUILD_TRAM], | ||||
| 	@"openttd.build_docks":     [NSNumber numberWithInt:MTHK_BUILD_DOCKS], | ||||
| 	@"openttd.build_airport":   [NSNumber numberWithInt:MTHK_BUILD_AIRPORT], | ||||
| }; | ||||
|  | ||||
| static NSDictionary *touchBarFallbackText = @{ | ||||
| 	@"openttd.pause":           @"Pause", | ||||
| 	@"openttd.fastforward":     @"Fast Forward", | ||||
| 	@"openttd.zoom_in":         @"Zoom In", | ||||
| 	@"openttd.zoom_out":        @"Zoom Out", | ||||
| 	@"openttd.build_rail":      @"Rail", | ||||
| 	@"openttd.build_road":      @"Road", | ||||
| 	@"openttd.build_tram":      @"Tram", | ||||
| 	@"openttd.build_docks":     @"Docks", | ||||
| 	@"openttd.build_airport":   @"Airport", | ||||
| }; | ||||
| #endif | ||||
|  | ||||
| /** Subclass of NSWindow to cater our special needs */ | ||||
| #ifdef HAVE_OSX_1015_SDK | ||||
| @interface OTTD_CocoaWindow : NSWindow <NSTouchBarDelegate> | ||||
| @property (strong) NSSet *touchbarItems; | ||||
| - (NSImage*)generateImage:(int)spriteId; | ||||
| #else | ||||
| @interface OTTD_CocoaWindow : NSWindow | ||||
| #ifdef HAVE_TOUCHBAR_SUPPORT | ||||
| 	<NSTouchBarDelegate> | ||||
| #endif | ||||
|  | ||||
| - (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag driver:(VideoDriver_Cocoa *)drv; | ||||
|  | ||||
| - (void)setFrame:(NSRect)frameRect display:(BOOL)flag; | ||||
|  | ||||
| - (void)refreshSystemSprites; | ||||
| @end | ||||
|  | ||||
| /** Subclass of NSView to support mouse awareness and text input. */ | ||||
|   | ||||
| @@ -32,7 +32,11 @@ | ||||
| #include "../../gfx_func.h" | ||||
| #include "../../window_func.h" | ||||
| #include "../../window_gui.h" | ||||
| #include "spritecache.h" | ||||
| #include "../../spritecache.h" | ||||
| #include "../../toolbar_gui.h" | ||||
| #include <array> | ||||
|  | ||||
| #include "table/sprites.h" | ||||
|  | ||||
| /* Table data for key mapping. */ | ||||
| #include "cocoa_keys.h" | ||||
| @@ -56,6 +60,31 @@ | ||||
|  * Read http://developer.apple.com/releasenotes/Cocoa/Objective-C++.html for more information. | ||||
|  */ | ||||
|  | ||||
| #ifdef HAVE_TOUCHBAR_SUPPORT | ||||
| struct TouchBarButton { | ||||
| 	NSTouchBarItemIdentifier key; | ||||
| 	SpriteID                 sprite; | ||||
| 	MainToolbarHotkeys       hotkey; | ||||
| 	NSString                *fallback_text; | ||||
|  | ||||
| 	bool operator ==(const NSTouchBarItemIdentifier other) const { return this->key == other; } | ||||
| }; | ||||
|  | ||||
| /* 9 items can be displayed on the touch bar when using default buttons. */ | ||||
| static const std::array<TouchBarButton, 9> _touchbar_buttons{{ | ||||
| 	{ @"openttd.pause",         SPR_IMG_PAUSE,       MTHK_PAUSE,         @"Pause" }, | ||||
| 	{ @"openttd.fastforward",   SPR_IMG_FASTFORWARD, MTHK_FASTFORWARD,   @"Fast Forward" }, | ||||
| 	{ @"openttd.zoom_in",       SPR_IMG_ZOOMIN,      MTHK_ZOOM_IN,       @"Zoom In" }, | ||||
| 	{ @"openttd.zoom_out",      SPR_IMG_ZOOMOUT,     MTHK_ZOOM_OUT,      @"Zoom Out" }, | ||||
| 	{ @"openttd.build_rail",    SPR_IMG_BUILDRAIL,   MTHK_BUILD_RAIL,    @"Rail" }, | ||||
| 	{ @"openttd.build_road",    SPR_IMG_BUILDROAD,   MTHK_BUILD_ROAD,    @"Road" }, | ||||
| 	{ @"openttd.build_tram",    SPR_IMG_BUILDTRAMS,  MTHK_BUILD_TRAM,    @"Tram" }, | ||||
| 	{ @"openttd.build_docks",   SPR_IMG_BUILDWATER,  MTHK_BUILD_DOCKS,   @"Docks" }, | ||||
| 	{ @"openttd.build_airport", SPR_IMG_BUILDAIR,    MTHK_BUILD_AIRPORT, @"Airport" } | ||||
| }}; | ||||
|  | ||||
| #endif | ||||
|  | ||||
| bool _allow_hidpi_window = true; // Referenced from table/misc_settings.ini | ||||
|  | ||||
| @interface OTTDMain : NSObject <NSApplicationDelegate> | ||||
| @@ -132,6 +161,37 @@ static std::vector<WChar> NSStringToUTF32(NSString *s) | ||||
| 	return unicode_str; | ||||
| } | ||||
|  | ||||
| static void CGDataFreeCallback(void *, const void *data, size_t) | ||||
| { | ||||
| 	delete[] (const uint32 *)data; | ||||
| } | ||||
|  | ||||
| /** | ||||
|  * Render an OTTD sprite to a Cocoa image. | ||||
|  * @param sprite_id Sprite to make a NSImage from. | ||||
|  * @param zoom Zoom level to render the sprite in. | ||||
|  * @return Autorelease'd image or nullptr on any error. | ||||
|  */ | ||||
| static NSImage *NSImageFromSprite(SpriteID sprite_id, ZoomLevel zoom) | ||||
| { | ||||
| 	if (!SpriteExists(sprite_id)) return nullptr; | ||||
|  | ||||
| 	/* Fetch the sprite and create a new bitmap */ | ||||
| 	Dimension dim = GetSpriteSize(sprite_id, nullptr, zoom); | ||||
| 	std::unique_ptr<uint32[]> buffer = DrawSpriteToRgbaBuffer(sprite_id, zoom); | ||||
| 	if (!buffer) return nullptr; // Failed to blit sprite for some reason. | ||||
|  | ||||
| 	CFAutoRelease<CGDataProvider> data(CGDataProviderCreateWithData(nullptr, buffer.release(), dim.width * dim.height * 4, &CGDataFreeCallback)); | ||||
| 	if (!data) return nullptr; | ||||
|  | ||||
| 	CGBitmapInfo info = kCGImageAlphaFirst | kCGBitmapByteOrder32Host; | ||||
| 	CFAutoRelease<CGColorSpaceRef> color_space(CGColorSpaceCreateWithName(kCGColorSpaceSRGB)); | ||||
| 	CFAutoRelease<CGImage> bitmap(CGImageCreate(dim.width, dim.height, 8, 32, dim.width * 4, color_space.get(), info, data.get(), nullptr, false, kCGRenderingIntentDefault)); | ||||
| 	if (!bitmap) return nullptr; | ||||
|  | ||||
| 	return [ [ [ NSImage alloc ] initWithCGImage:bitmap.get() size:NSZeroSize ] autorelease ]; | ||||
| } | ||||
|  | ||||
|  | ||||
| /** | ||||
|  * The main class of the application, the application's delegate. | ||||
| @@ -383,6 +443,7 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel | ||||
|  | ||||
| @implementation OTTD_CocoaWindow { | ||||
| 	VideoDriver_Cocoa *driver; | ||||
| 	bool touchbar_created; | ||||
| } | ||||
|  | ||||
| /** | ||||
| @@ -392,6 +453,7 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel | ||||
| { | ||||
| 	if (self = [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ]) { | ||||
| 		self->driver = drv; | ||||
| 		self->touchbar_created = false; | ||||
|  | ||||
| 		[ self setContentMinSize:NSMakeSize(64.0f, 64.0f) ]; | ||||
|  | ||||
| @@ -405,87 +467,6 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel | ||||
| 	return self; | ||||
| } | ||||
|  | ||||
| #ifdef HAVE_OSX_1015_SDK | ||||
|  | ||||
| - (void)touchBarButtonAction:(id)sender | ||||
| { | ||||
| 	if (@available(macOS 10.15, *)) { | ||||
| 		NSButtonTouchBarItem *btn = (NSButtonTouchBarItem *)sender; | ||||
| 		NSNumber *hotkeyIndex = [ touchBarButtonActions objectForKey:btn.identifier ]; | ||||
| 		HandleToolbarHotkey(hotkeyIndex.intValue); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| #pragma mark NSTouchBarProvider | ||||
| - (nullable NSTouchBar *)makeTouchBar | ||||
| { | ||||
| 	NSTouchBar *bar = [ [ NSTouchBar alloc ] init ]; | ||||
| 	bar.delegate = self; | ||||
| 	bar.defaultItemIdentifiers = touchBarButtonIdentifiers; | ||||
|  | ||||
| 	return bar; | ||||
| } | ||||
|  | ||||
| -(NSImage *)generateImage:(int)spriteId | ||||
| { | ||||
| 	if (!SpriteExists(spriteId)) { | ||||
| 		return nullptr; | ||||
| 	} | ||||
|  | ||||
| 	/* Fetch the sprite and create a new bitmap */ | ||||
| 	const Sprite *fullspr = GetSprite(spriteId, ST_NORMAL); | ||||
| 	const std::unique_ptr<uint32[]> buffer = DrawSpriteToRgbaBuffer(spriteId); | ||||
| 	if (!buffer) { | ||||
| 		return nullptr; // failed to blit sprite or we're using an 8bpp blitter. | ||||
| 	} | ||||
|  | ||||
| 	NSBitmapImageRep *bitmap = [ [ NSBitmapImageRep alloc ] initWithBitmapDataPlanes:nil pixelsWide:fullspr->width pixelsHigh:fullspr->height bitsPerSample:8 samplesPerPixel:4 hasAlpha:YES isPlanar:NO colorSpaceName:NSCalibratedRGBColorSpace bytesPerRow:0 bitsPerPixel:0 ]; | ||||
|  | ||||
| 	/* Copy the sprite to the NSBitmapImageRep image buffer */ | ||||
| 	const Colour *src = (const Colour *)buffer.get(); | ||||
| 	for (int y = 0; y < fullspr->height; y++) { | ||||
| 		for (int x = 0; x < fullspr->width; x++) { | ||||
| 			NSUInteger pixel[4]; | ||||
| 			pixel[0] = src->r; | ||||
| 			pixel[1] = src->g; | ||||
| 			pixel[2] = src->b; | ||||
| 			pixel[3] = src->a; | ||||
| 			[ bitmap setPixel:pixel atX:x y:y ]; | ||||
|  | ||||
| 			src += 1; | ||||
| 		} | ||||
| 	} | ||||
|  | ||||
| 	/* Finally, convert the NSBitmapImageRep we created to a NSimage we can put on the button and clean up. */ | ||||
| 	NSImage *outImage = [ [ NSImage alloc ] initWithSize:NSMakeSize(fullspr->width, fullspr->height) ]; | ||||
| 	[ outImage addRepresentation:bitmap ]; | ||||
| 	[ bitmap release ]; | ||||
|  | ||||
| 	return outImage; | ||||
| } | ||||
|  | ||||
| #pragma mark NSTouchBarDelegate | ||||
| - (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier | ||||
| { | ||||
| 	if (@available(macOS 10.15, *)) { | ||||
| 		NSButtonTouchBarItem *button = [ [ NSButtonTouchBarItem alloc ] initWithIdentifier:identifier ]; | ||||
| 		button.target = self; | ||||
| 		button.action = @selector(touchBarButtonAction:); | ||||
|  | ||||
| 		NSNumber *num = touchBarButtonSprites[identifier]; | ||||
| 		NSImage *generatedImage = [ self generateImage:num.unsignedIntValue ]; | ||||
| 		if (generatedImage != nullptr) { | ||||
| 			button.image = generatedImage; | ||||
| 		} else { | ||||
| 			button.title = NSLocalizedString(touchBarFallbackText[identifier], @""); | ||||
| 		} | ||||
| 		return button; | ||||
| 	} else { | ||||
| 		return nullptr; | ||||
| 	} | ||||
| } | ||||
| #endif | ||||
|  | ||||
| /** | ||||
|  * Define the rectangle we draw our window in | ||||
|  */ | ||||
| @@ -496,6 +477,83 @@ void CocoaDialog(const char *title, const char *message, const char *buttonLabel | ||||
| 	driver->AllocateBackingStore(); | ||||
| } | ||||
|  | ||||
| #ifdef HAVE_TOUCHBAR_SUPPORT | ||||
|  | ||||
| - (void)touchBarButtonAction:(id)sender | ||||
| { | ||||
| 	NSButton *btn = (NSButton *)sender; | ||||
| 	if (auto item = std::find(_touchbar_buttons.cbegin(), _touchbar_buttons.cend(), (NSTouchBarItemIdentifier)btn.identifier); item != _touchbar_buttons.cend()) { | ||||
| 		HandleToolbarHotkey(item->hotkey); | ||||
| 	} | ||||
| } | ||||
|  | ||||
| - (nullable NSTouchBar *)makeTouchBar | ||||
| { | ||||
| 	/* Make button identifier array. */ | ||||
| 	NSMutableArray<NSTouchBarItemIdentifier> *button_ids = [ [ NSMutableArray alloc ] init ]; | ||||
| 	for (const auto &button : _touchbar_buttons) { | ||||
| 		[ button_ids addObject:button.key ]; | ||||
| 	} | ||||
| 	[ button_ids addObject:NSTouchBarItemIdentifierOtherItemsProxy ]; | ||||
|  | ||||
| 	NSTouchBar *bar = [ [ NSTouchBar alloc ] init ]; | ||||
| 	bar.delegate = self; | ||||
| 	bar.defaultItemIdentifiers = button_ids; | ||||
| 	[ button_ids release ]; | ||||
|  | ||||
| 	self->touchbar_created = true; | ||||
|  | ||||
| 	return bar; | ||||
| } | ||||
|  | ||||
| - (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier | ||||
| { | ||||
| 	auto item = std::find(_touchbar_buttons.cbegin(), _touchbar_buttons.cend(), identifier); | ||||
| 	assert(item != _touchbar_buttons.cend()); | ||||
|  | ||||
| 	NSButton *button = [ NSButton buttonWithTitle:item->fallback_text target:self action:@selector(touchBarButtonAction:) ]; | ||||
| 	button.identifier = identifier; | ||||
| 	button.imageScaling = NSImageScaleProportionallyDown; | ||||
|  | ||||
| 	NSCustomTouchBarItem *tb_item = [ [ NSCustomTouchBarItem alloc] initWithIdentifier:identifier ]; | ||||
| 	tb_item.view = button; | ||||
| 	return tb_item; | ||||
| } | ||||
|  | ||||
| #endif /* HAVE_TOUCHBAR_SUPPORT */ | ||||
|  | ||||
| - (void)refreshSystemSprites | ||||
| { | ||||
| #ifdef HAVE_TOUCHBAR_SUPPORT | ||||
| 	if (!self->touchbar_created || ![ self respondsToSelector:@selector(touchBar) ] || self.touchBar == nil) return; | ||||
|  | ||||
| 	/* Re-create button images from OTTD sprites. */ | ||||
| 	for (NSTouchBarItemIdentifier ident in self.touchBar.itemIdentifiers) { | ||||
| 		auto item = std::find(_touchbar_buttons.cbegin(), _touchbar_buttons.cend(), ident); | ||||
| 		if (item == _touchbar_buttons.cend()) continue; | ||||
|  | ||||
| 		NSCustomTouchBarItem *tb_item = [ self.touchBar itemForIdentifier:ident ]; | ||||
| 		NSButton *button = tb_item.view; | ||||
|  | ||||
| 		NSImage *image = NSImageFromSprite(item->sprite, _settings_client.gui.zoom_min); | ||||
| 		if (image != nil) { | ||||
| 			/* Human Interface Guidelines: Maximum touch bar glyph size 22 pt. */ | ||||
| 			CGFloat max_dim = std::max(image.size.width, image.size.height); | ||||
| 			if (max_dim > 0.0) { | ||||
| 				CGFloat scale = 22.0 / max_dim; | ||||
| 				image.size = NSMakeSize(image.size.width * scale, image.size.height * scale); | ||||
| 			} | ||||
|  | ||||
| 			button.image = image; | ||||
| 			button.imagePosition = NSImageOnly; | ||||
| 		} else { | ||||
| 			button.image = nil; | ||||
| 			button.imagePosition = NSNoImage; | ||||
| 		} | ||||
| 	} | ||||
| #endif /* HAVE_TOUCHBAR_SUPPORT */ | ||||
| } | ||||
|  | ||||
| @end | ||||
|  | ||||
| @implementation OTTD_CocoaView { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Jonathan G Rennison
					Jonathan G Rennison