20#if defined(WITH_COCOA) || defined(DOXYGEN_API)
47 NSTouchBarItemIdentifier
key;
58 {
@"openttd.pause", SPR_IMG_PAUSE, MTHK_PAUSE,
@"Pause" },
59 {
@"openttd.fastforward", SPR_IMG_FASTFORWARD, MTHK_FASTFORWARD,
@"Fast Forward" },
60 {
@"openttd.zoom_in", SPR_IMG_ZOOMIN, MTHK_ZOOM_IN,
@"Zoom In" },
61 {
@"openttd.zoom_out", SPR_IMG_ZOOMOUT, MTHK_ZOOM_OUT,
@"Zoom Out" },
62 {
@"openttd.build_rail", SPR_IMG_BUILDRAIL, MTHK_BUILD_RAIL,
@"Rail" },
63 {
@"openttd.build_road", SPR_IMG_BUILDROAD, MTHK_BUILD_ROAD,
@"Road" },
64 {
@"openttd.build_tram", SPR_IMG_BUILDTRAMS, MTHK_BUILD_TRAM,
@"Tram" },
65 {
@"openttd.build_docks", SPR_IMG_BUILDWATER, MTHK_BUILD_DOCKS,
@"Docks" },
66 {
@"openttd.build_airport", SPR_IMG_BUILDAIR, MTHK_BUILD_AIRPORT,
@"Airport" }
71@interface OTTDMain : NSObject <NSApplicationDelegate>
92 i += c < 0x10000 ? 1 : 2;
106 auto it = view.begin();
107 const auto end = view.end();
108 for (NSUInteger i = 0; it != end && i < count; ++it) {
109 i += *it < 0x10000 ? 1 : 2;
111 return it.GetByteOffset();
121 std::vector<char32_t> unicode_str;
124 for (NSUInteger i = 0; i < s.length; i++) {
125 unichar c = [ s characterAtIndex:i ];
132 unicode_str.push_back(c);
145 delete[] (
const uint32_t *)data;
156 if (!SpriteExists(sprite_id))
return nullptr;
161 if (!buffer)
return nullptr;
164 if (!data)
return nullptr;
166 CGBitmapInfo info = kCGImageAlphaFirst | kCGBitmapByteOrder32Host;
168 CFAutoRelease<CGImage> bitmap(CGImageCreate(dim.width, dim.height, 8, 32, dim.width * 4, colour_space.get(), info, data.get(),
nullptr,
false, kCGRenderingIntentDefault));
169 if (!bitmap)
return nullptr;
171 return [ [ [ NSImage alloc ] initWithCGImage:bitmap.get() size:NSZeroSize ] autorelease ];
178@implementation OTTDMain
187 NSEventType type = NSEventTypeApplicationDefined;
188 NSEvent *
event = [
NSEvent otherEventWithType:type location:NSMakePoint(0, 0) modifierFlags:0 timestamp:0.0 windowNumber:0 context:nil subtype:0 data1:0 data2:0 ];
189 [
NSApp postEvent:event atStart:YES ];
195- (void)launchGameEngine: (NSNotification*) note
200 NSEvent *e = [ [
NSEvent alloc ] init ];
201 [
drv->cocoaview cursorUpdate:e ];
208 [
self performSelectorOnMainThread:@selector(stopEngine) withObject:nil waitUntilDone:FALSE ];
214- (void) applicationDidFinishLaunching: (NSNotification*) note
217 [ [
NSNotificationCenter defaultCenter ] addObserver:
self selector:@selector(launchGameEngine:) name:OTTDMainLaunchGameEngine object:nil ];
220 [ [
NSNotificationCenter defaultCenter ] postNotificationName:OTTDMainLaunchGameEngine object:nil ];
226- (NSApplicationTerminateReply)applicationShouldTerminate:(NSApplication*) sender
228 HandleExitGameRequest();
230 return NSTerminateCancel;
248- (BOOL)applicationSupportsSecureRestorableState:(NSApplication*) sender
259 NSString *appName =
@"OpenTTD";
260 NSMenu *appleMenu = [ [ NSMenu alloc ] initWithTitle:appName ];
263 NSString *title = [ @"About " stringByAppendingString:appName ];
264 [ appleMenu addItemWithTitle:title action:@selector(orderFrontStandardAboutPanel:) keyEquivalent:@"" ];
266 [ appleMenu addItem:[ NSMenuItem separatorItem ] ];
268 title = [ @"Hide " stringByAppendingString:appName ];
269 [ appleMenu addItemWithTitle:title action:@selector(hide:) keyEquivalent:@"h" ];
271 NSMenuItem *menuItem = [ appleMenu addItemWithTitle:@"Hide Others" action:@selector(hideOtherApplications:) keyEquivalent:@"h" ];
272 [ menuItem setKeyEquivalentModifierMask:(NSEventModifierFlagOption | NSEventModifierFlagCommand) ];
274 [ appleMenu addItemWithTitle:@"Show All" action:@selector(unhideAllApplications:) keyEquivalent:@"" ];
276 [ appleMenu addItem:[ NSMenuItem separatorItem ] ];
278 title = [ @"Quit " stringByAppendingString:appName ];
279 [ appleMenu addItemWithTitle:title action:@selector(terminate:) keyEquivalent:@"q" ];
282 menuItem = [ [ NSMenuItem alloc ] initWithTitle:@"" action:nil keyEquivalent:@"" ];
283 [ menuItem setSubmenu:appleMenu ];
284 [ [ NSApp mainMenu ] addItem:menuItem ];
289 if ([ NSApp respondsToSelector:
@selector(setAppleMenu:) ]) {
290 [ NSApp performSelector:@selector(setAppleMenu:) withObject:appleMenu ];
294 [ appleMenu release ];
295 [ menuItem release ];
303 NSMenu *windowMenu = [ [ NSMenu alloc ] initWithTitle:@"Window" ];
306 [ windowMenu addItemWithTitle:@"Minimize" action:@selector(performMiniaturize:) keyEquivalent:@"m" ];
309 NSMenuItem *menuItem = [ [ NSMenuItem alloc ] initWithTitle:@"Window" action:nil keyEquivalent:@"" ];
310 [ menuItem setSubmenu:windowMenu ];
311 [ [ NSApp mainMenu ] addItem:menuItem ];
314 [ windowMenu addItemWithTitle:@"Fullscreen" action:@selector(toggleFullScreen:) keyEquivalent:@"^f" ];
317 [ NSApp setWindowsMenu:windowMenu ];
320 [ windowMenu release ];
321 [ menuItem release ];
330 ProcessSerialNumber psn = { 0, kCurrentProcess };
333 [ NSApplication sharedApplication ];
336 OSStatus returnCode = TransformProcessType(&psn, kProcessTransformToForegroundApplication);
337 if (returnCode != 0)
Debug(driver, 0,
"Could not change to foreground application. Error {}", (
int)returnCode);
340 if ([ NSWindow respondsToSelector:
@selector(setAllowsAutomaticWindowTabbing:) ]) {
342 [
NSWindow performSelector:@selector(setAllowsAutomaticWindowTabbing:) withObject:nil];
346 [ [ NSApplication sharedApplication ] setActivationPolicy:NSApplicationActivationPolicyRegular ];
347 [ [ NSApplication sharedApplication ] activateIgnoringOtherApps:YES ];
350 [ NSApp setMainMenu:[ [ NSMenu alloc ] init ] ];
356 [ NSApp setDelegate:_ottd_main ];
379void CocoaDialog(std::string_view title, std::string_view message, std::string_view buttonLabel)
387 fmt::print(stderr,
"{}: {}\n", title, message);
392 NSAlert *alert = [ [ NSAlert alloc ] init ];
393 [ alert setAlertStyle: NSAlertStyleCritical ];
394 [ alert setMessageText:[ [ NSString alloc ] initWithBytes:title.data() length:title.size() encoding:NSUTF8StringEncoding ] ];
395 [ alert setInformativeText:[ [ NSString alloc ] initWithBytes:message.data() length:message.size() encoding:NSUTF8StringEncoding ] ];
396 [ alert addButtonWithTitle: [ [ NSString alloc ] initWithBytes:buttonLabel.data() length:buttonLabel.size() encoding:NSUTF8StringEncoding ] ];
410@implementation NSCursor (OTTD_CocoaCursor)
418 unsigned char clearGIFBytes[] = {
419 0x47, 0x49, 0x46, 0x38, 0x37, 0x61, 0x10, 0x00, 0x10, 0x00, 0x80, 0x00,
420 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x21, 0xF9, 0x04, 0x01, 0x00,
421 0x00, 0x01, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00,
422 0x00, 0x02, 0x0E, 0x8C, 0x8F, 0xA9, 0xCB, 0xED, 0x0F, 0xA3, 0x9C, 0xB4,
423 0xDA, 0x8B, 0xB3, 0x3E, 0x05, 0x00, 0x3B};
424 NSData *clearGIFData = [
NSData dataWithBytesNoCopy:&clearGIFBytes[0] length:55 freeWhenDone:NO ];
425 NSImage *clearImg = [ [
NSImage alloc ] initWithData:clearGIFData ];
426 return [ [
NSCursor alloc ] initWithImage:clearImg hotSpot:NSMakePoint(0.0,0.0) ];
438- (instancetype)initWithContentRect:(NSRect)contentRect styleMask:(NSUInteger)styleMask backing:(NSBackingStoreType)backingType defer:(BOOL)flag driver:(
VideoDriver_Cocoa *)drv
440 if (self = [ super initWithContentRect:contentRect styleMask:styleMask backing:backingType defer:flag ]) {
442 self->touchbar_created =
false;
444 [
self setContentMinSize:NSMakeSize(64.0f, 64.0f) ];
447 NSString *nsscaption = [ [
NSString alloc ] initWithBytes:caption.data() length:caption.size() encoding:NSUTF8StringEncoding ];
448 [
self setTitle:nsscaption ];
449 [
self setMiniwindowTitle:nsscaption ];
459- (void)setFrame:(NSRect)frameRect display:(BOOL)flag
461 [
super setFrame:frameRect display:flag ];
463 driver->AllocateBackingStore();
466- (void)touchBarButtonAction:(
id)sender
468 NSButton *btn = (NSButton *)sender;
474- (nullable NSTouchBar *)makeTouchBar
477 NSMutableArray<NSTouchBarItemIdentifier> *button_ids = [ [ NSMutableArray alloc ] init ];
479 [ button_ids addObject:button.key ];
481 [ button_ids addObject:NSTouchBarItemIdentifierOtherItemsProxy ];
483 NSTouchBar *bar = [ [ NSTouchBar alloc ] init ];
485 bar.defaultItemIdentifiers = button_ids;
486 [ button_ids release ];
488 self->touchbar_created =
true;
493- (nullable NSTouchBarItem *)touchBar:(NSTouchBar *)touchBar makeItemForIdentifier:(NSTouchBarItemIdentifier)identifier
498 NSButton *button = [ NSButton buttonWithTitle:item->fallback_text target:
self action:@selector(touchBarButtonAction:) ];
499 button.identifier = identifier;
500 button.imageScaling = NSImageScaleProportionallyDown;
502 NSCustomTouchBarItem *tb_item = [ [ NSCustomTouchBarItem alloc] initWithIdentifier:identifier ];
503 tb_item.view = button;
507- (void)refreshSystemSprites
509 if (!self->touchbar_created || ![ self respondsToSelector:
@selector(touchBar) ] || self.touchBar == nil)
return;
512 for (NSTouchBarItemIdentifier ident in self.touchBar.itemIdentifiers) {
516 NSCustomTouchBarItem *tb_item = [
self.touchBar itemForIdentifier:ident ];
517 NSButton *button = tb_item.view;
522 CGFloat max_dim = std::max(image.size.width, image.size.height);
524 CGFloat scale = 22.0 / max_dim;
525 image.size = NSMakeSize(image.size.width * scale, image.size.height * scale);
528 button.image = image;
529 button.imagePosition = NSImageOnly;
532 button.imagePosition = NSNoImage;
540 float _current_magnification;
546- (instancetype)initWithFrame:(NSRect)frameRect
548 if (self = [ super initWithFrame:frameRect ]) {
549 self->_use_hidpi =
_allow_hidpi_window && [
self respondsToSelector:@selector(convertRectToBacking:) ] && [
self respondsToSelector:@selector(convertRectFromBacking:) ];
554- (NSRect)getRealRect:(NSRect)rect
556 return _use_hidpi ? [
self convertRectToBacking:rect ] : rect;
559- (NSRect)getVirtualRect:(NSRect)rect
561 return _use_hidpi ? [
self convertRectFromBacking:rect ] : rect;
564- (CGFloat)getContentsScale
566 return _use_hidpi && self.window != nil ? [
self.window backingScaleFactor ] : 1.0f;
577- (void)setNeedsDisplayInRect:(NSRect)invalidRect
580 for ( NSView *v in [ self subviews ]) {
581 [ v setNeedsDisplayInRect:[ v convertRect:invalidRect fromView:
self ] ];
586- (void)cursorUpdate:(NSEvent *)event
588 [ (_game_mode == GM_BOOTSTRAP ? [
NSCursor arrowCursor ] : [
NSCursor clearCocoaCursor ]) set ];
591- (void)viewWillMoveToWindow:(NSWindow *)win
593 for (NSTrackingArea *a in [ self trackingAreas ]) {
594 [
self removeTrackingArea:a ];
598- (void)viewDidMoveToWindow
601 NSTrackingAreaOptions track_opt = NSTrackingInVisibleRect | NSTrackingActiveInActiveApp | NSTrackingMouseEnteredAndExited | NSTrackingMouseMoved | NSTrackingCursorUpdate;
602 NSTrackingArea *track = [ [ NSTrackingArea alloc ] initWithRect:[
self bounds ] options:track_opt owner:
self userInfo:nil ];
603 [
self addTrackingArea:track ];
610- (void)mouseEntered:(NSEvent *)theEvent
612 _cursor.in_window =
true;
618- (void)mouseExited:(NSEvent *)theEvent
620 if ([ self window ] != nil) UndrawMouseCursor();
621 _cursor.in_window =
false;
629- (NSPoint)mousePositionFromEvent:(NSEvent *)e
631 NSPoint pt = e.locationInWindow;
632 if ([ e window ] == nil) pt = [
self.
window convertRectFromScreen:NSMakeRect(pt.x, pt.y, 0, 0) ].origin;
633 pt = [
self convertPoint:pt fromView:nil ];
635 return [
self getRealRect:NSMakeRect(pt.x,
self.bounds.size.height - pt.y, 0, 0) ].origin;
642- (void)internalMouseMoveEvent:(NSEvent *)event
644 if (_cursor.fix_at) {
645 _cursor.UpdateCursorPositionRelative(event.deltaX * self.getContentsScale, event.deltaY * self.getContentsScale);
648 _cursor.UpdateCursorPosition(pt.x, pt.y);
659 bool cur_fix = _cursor.fix_at;
663 if (cur_fix != _cursor.fix_at) CGAssociateMouseAndMouseCursorPosition(!_cursor.fix_at);
671- (BOOL)emulateRightButton:(NSEvent *)event
673 uint32_t keymask = 0;
674 if (
_settings_client.gui.right_mouse_btn_emulation == RMBE_COMMAND) keymask |= NSEventModifierFlagCommand;
675 if (
_settings_client.gui.right_mouse_btn_emulation == RMBE_CONTROL) keymask |= NSEventModifierFlagControl;
677 return (event.modifierFlags & keymask) != 0;
684- (void)mouseMoved:(NSEvent *)event
693- (void)mouseDragged:(NSEvent *)event
702- (void)mouseDown:(NSEvent *)event
704 if ([ self emulateRightButton:event ]) {
705 self->_emulated_down =
true;
717- (void)mouseUp:(NSEvent *)event
719 if (self->_emulated_down) {
720 self->_emulated_down =
false;
733- (void)rightMouseDragged:(NSEvent *)event
742- (void)rightMouseDown:(NSEvent *)event
753- (void)rightMouseUp:(NSEvent *)event
763- (void)scrollWheel:(NSEvent *)event
765 if ([ event deltaY ] > 0.0) {
767 }
else if ([ event deltaY ] < 0.0) {
776 if ([ event respondsToSelector:
@selector(hasPreciseScrollingDeltas) ]) {
778 if (![ event hasPreciseScrollingDeltas ])
return;
780 deltaX = [
event scrollingDeltaX ] * 0.5f;
781 deltaY = [
event scrollingDeltaY ] * 0.5f;
783 deltaX = [
event deltaX ] * 5;
784 deltaY = [
event deltaY ] * 5;
787 _cursor.h_wheel -=
static_cast<float>(deltaX *
_settings_client.gui.scrollwheel_multiplier);
788 _cursor.v_wheel -=
static_cast<float>(deltaY *
_settings_client.gui.scrollwheel_multiplier);
789 _cursor.wheel_moved =
true;
796- (void)magnifyWithEvent:(NSEvent *)event
799 self->_current_magnification += [
event magnification ] * 5.0f;
801 while (self->_current_magnification >= 1.0f) {
802 self->_current_magnification -= 1.0f;
806 while (self->_current_magnification <= -1.0f) {
807 self->_current_magnification += 1.0f;
817- (void)endGestureWithEvent:(NSEvent *)event
820 self->_current_magnification = 0.0f;
832- (BOOL)internalHandleKeycode:(
unsigned short)keycode unicode:(
char32_t)unicode pressed:(BOOL)down modifiers:(NSUInteger)modifiers
836 case QZ_DOWN:
SB(
_dirkeys, 3, 1, down);
break;
837 case QZ_LEFT:
SB(
_dirkeys, 0, 1, down);
break;
838 case QZ_RIGHT:
SB(
_dirkeys, 2, 1, down);
break;
849 if (down && (modifiers & NSEventModifierFlagCommand)) {
855 if (down &&
EditBoxInGlobalFocus() && (modifiers & (NSEventModifierFlagCommand | NSEventModifierFlagControl))) {
860 if (down &&
EditBoxInGlobalFocus() && (modifiers & (NSEventModifierFlagCommand | NSEventModifierFlagControl))) {
866 BOOL interpret_keys = YES;
869 auto vk = std::ranges::find(_vk_mapping, keycode, &CocoaVkMapping::vk_from);
870 uint32_t pressed_key = vk != std::end(_vk_mapping) ? vk->map_to : 0;
872 if (modifiers & NSEventModifierFlagShift) pressed_key |= WKC_SHIFT;
873 if (modifiers & NSEventModifierFlagControl) pressed_key |= (
_settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_CTRL : WKC_META);
874 if (modifiers & NSEventModifierFlagOption) pressed_key |= WKC_ALT;
875 if (modifiers & NSEventModifierFlagCommand) pressed_key |= (
_settings_client.gui.right_mouse_btn_emulation != RMBE_CONTROL ? WKC_META : WKC_CTRL);
877 static bool console =
false;
880 if (pressed_key == WKC_BACKQUOTE && (console || unicode == 0)) {
896 Debug(driver, 3,
"cocoa_v: QZ_KeyEvent: {:x} ({:x}), down, mapping: {:x}", keycode, (
int)unicode, pressed_key);
898 Debug(driver, 3,
"cocoa_v: QZ_KeyEvent: {:x} ({:x}), up", keycode, (
int)unicode);
901 return interpret_keys;
908- (void)keyDown:(NSEvent *)event
911 switch (event.keyCode) {
915 if (event.modifierFlags & NSEventModifierFlagCommand) {
916 [
self interpretKeyEvents:[
NSArray arrayWithObject:event ] ];
922 std::vector<char32_t> unicode_str =
NSStringToUTF32([ event characters ]);
923 if (unicode_str.empty()) unicode_str.push_back(0);
926 if ([ self internalHandleKeycode:event.keyCode unicode:unicode_str[0] pressed:YES modifiers:event.modifierFlags ]) {
927 [
self interpretKeyEvents:[
NSArray arrayWithObject:event ] ];
931 for (
size_t i = 1; i < unicode_str.size(); i++) {
941- (void)keyUp:(NSEvent *)event
944 switch (event.keyCode) {
948 if (event.modifierFlags & NSEventModifierFlagCommand) {
949 [
self interpretKeyEvents:[
NSArray arrayWithObject:event ] ];
955 std::vector<char32_t> unicode_str =
NSStringToUTF32([ event characters ]);
956 if (unicode_str.empty()) unicode_str.push_back(0);
965- (void)flagsChanged:(NSEvent *)event
967 const int mapping[] = { QZ_CAPSLOCK, QZ_LSHIFT, QZ_LCTRL, QZ_LALT, QZ_LMETA };
969 NSUInteger newMods =
event.modifierFlags;
970 if (self->_current_mods == newMods)
return;
973 for (
unsigned int i = 0, bit = NSEventModifierFlagCapsLock; bit <= NSEventModifierFlagCommand; bit <<= 1, ++i) {
974 unsigned int currentMask, newMask;
976 currentMask = self->_current_mods & bit;
977 newMask = newMods & bit;
979 if (currentMask && currentMask != newMask) {
981 }
else if (newMask && currentMask != newMask) {
991- (void)insertText:(
id)aString replacementRange:(NSRange)replacementRange
997 std::optional<size_t> insert_point;
998 std::optional<size_t> replace_range;
999 if (replacementRange.location != NSNotFound) {
1001 std::string_view focused_text{_focused_window->GetFocusedTextbuf()->GetText()};
1003 replace_range = *insert_point +
Utf8AdvanceByUtf16Units(focused_text.substr(*insert_point), replacementRange.length);
1007 HandleTextInput([ s UTF8String ],
false, std::nullopt, insert_point, replace_range);
1011- (void)insertText:(
id)aString
1017- (void)setMarkedText:(
id)aString selectedRange:(NSRange)selRange replacementRange:(NSRange)replacementRange
1023 const char *utf8 = [
s UTF8String ];
1024 if (utf8 !=
nullptr) {
1025 std::optional<size_t> insert_point;
1026 std::optional<size_t> replace_range;
1027 if (replacementRange.location != NSNotFound) {
1030 std::string_view focused_text{_focused_window->GetFocusedTextbuf()->GetText()};
1031 insert_point =
Utf8AdvanceByUtf16Units(focused_text, replacementRange.location + (marked.location != NSNotFound ? marked.location : 0u));
1032 replace_range = *insert_point +
Utf8AdvanceByUtf16Units(focused_text.substr(*insert_point), replacementRange.length);
1043- (void)setMarkedText:(
id)aString selectedRange:(NSRange)selRange
1059 const Textbuf *text_buf = _focused_window->GetFocusedTextbuf();
1060 std::string_view text = text_buf->
GetText();
1062 return NSMakeRange(start, 0);
1070 const Textbuf *text_buf = _focused_window->GetFocusedTextbuf();
1072 std::string_view text = text_buf->
GetText();
1076 return NSMakeRange(start, len);
1079 return NSMakeRange(NSNotFound, 0);
1087 return _focused_window->GetFocusedTextbuf()->markend != 0;
1091- (NSAttributedString *)attributedSubstringForProposedRange:(NSRange)theRange actualRange:(NSRangePointer)actualRange
1095 auto text = _focused_window->GetFocusedTextbuf()->GetText();
1096 NSString *s = [ [
NSString alloc] initWithBytes:text.data() length:text.size() encoding:NSUTF8StringEncoding ];
1097 NSRange valid_range = NSIntersectionRange(NSMakeRange(0, [ s length ]), theRange);
1099 if (actualRange !=
nullptr) *actualRange = valid_range;
1100 if (valid_range.length == 0)
return nil;
1102 return [ [ [
NSAttributedString alloc ] initWithString:[
s substringWithRange:valid_range ] ] autorelease ];
1106- (NSAttributedString *)attributedSubstringFromRange:(NSRange)theRange
1116 auto text = _focused_window->GetFocusedTextbuf()->GetText();
1117 return [ [ [
NSAttributedString alloc ] initWithString:[ [
NSString alloc ] initWithBytes:text.data() length:text.size() encoding:NSUTF8StringEncoding ] ] autorelease ];
1121- (NSUInteger)characterIndexForPoint:(NSPoint)thePoint
1125 NSPoint view_pt = [
self convertRect:[ [
self window ] convertRectFromScreen:NSMakeRect(thePoint.x, thePoint.y, 0, 0) ] fromView:nil ].origin;
1127 Point pt = { (int)view_pt.x, (
int)[
self frame ].size.height - (int)view_pt.y };
1129 auto index = _focused_window->GetTextCharacterAtPosition(pt);
1130 if (index == -1)
return NSNotFound;
1132 auto text = _focused_window->GetFocusedTextbuf()->GetText();
1137- (NSRect)firstRectForCharacterRange:(NSRange)aRange
1141 std::string_view focused_text = _focused_window->GetFocusedTextbuf()->GetText();
1147 Rect r = _focused_window->GetTextBoundingRect(start, end);
1148 NSRect view_rect = NSMakeRect(_focused_window->left + r.left, [ self frame ].size.height - _focused_window->top - r.bottom, r.right - r.left, r.bottom - r.top);
1150 return [ [
self window ] convertRectToScreen:[
self convertRect:view_rect toView:nil ] ];
1154- (NSRect)firstRectForCharacterRange:(NSRange)aRange actualRange:(NSRangePointer)actualRange
1169- (void)deleteBackward:(
id)sender
1178- (void)deleteWordBackward:(
id)sender
1187- (void)deleteForward:(
id)sender
1196- (void)deleteWordForward:(
id)sender
1205- (void)moveLeft:(
id)sender
1214- (void)moveWordLeft:(
id)sender
1223- (void)moveRight:(
id)sender
1232- (void)moveWordRight:(
id)sender
1241- (void)moveUp:(
id)sender
1250- (void)moveDown:(
id)sender
1259- (void)moveUpAndModifySelection:(
id)sender
1268- (void)moveDownAndModifySelection:(
id)sender
1277- (void)moveToBeginningOfLine:(
id)sender
1286- (void)moveToEndOfLine:(
id)sender
1295- (void)scrollPageUp:(
id)sender
1304- (void)scrollPageDown:(
id)sender
1313- (void)pageUpAndModifySelection:(
id)sender
1322- (void)pageDownAndModifySelection:(
id)sender
1331- (void)scrollToBeginningOfDocument:(
id)sender
1341- (void)scrollToEndOfDocument:(
id)sender
1351- (void)insertNewline:(
id)sender
1360- (void)cancelOperation:(
id)sender
1369- (void)doCommandBySelector:(
SEL)aSelector
1371 if ([ self respondsToSelector:aSelector ]) [
self performSelector:aSelector ];
1384 if (self = [ super init ]) {
1393- (BOOL)windowShouldClose:(
id)sender
1395 HandleExitGameRequest();
1400- (void)windowDidEnterFullScreen:(NSNotification *)aNotification
1402 NSPoint loc = [
driver->cocoaview convertPoint:[ [
aNotification object ] mouseLocationOutsideOfEventStream ] fromView:nil ];
1403 BOOL inside = ([
driver->cocoaview hitTest:loc ] == driver->cocoaview);
1407 NSEvent *e = [ [
NSEvent alloc ] init ];
1408 [
driver->cocoaview mouseEntered:e ];
1413- (void)windowDidChangeBackingProperties:(NSNotification *)notification
1418 driver->AllocateBackingStore();
1424- (NSApplicationPresentationOptions)window:(NSWindow *)window willUseFullScreenPresentationOptions:(NSApplicationPresentationOptions)proposedOptions
1426 return NSApplicationPresentationFullScreen | NSApplicationPresentationHideMenuBar | NSApplicationPresentationHideDock;
constexpr T SB(T &x, const uint8_t s, const uint8_t n, const U d)
Set n bits in x starting at bit s to d.
Re-implement the system cursor in order to allow hiding and showing it nicely.
NSCursor * clearCocoaCursor()
Create clear cursor for cocoa driver.
virtual void Stop()=0
Stop this driver.
Constant span of UTF-8 encoded data.
void MainLoopReal()
Main game loop.
virtual bool ToggleFullscreen(bool fullscreen)=0
Change the full screen setting.
static std::string GetCaption()
Get the caption to use for the game's title bar.
static VideoDriver * GetInstance()
Get the currently active instance of the video driver.
bool _cocoa_video_started
Is the Cocoa video driver running.
bool _tab_is_down
Is tab button pressed.
OS interface for the cocoa video driver.
bool _allow_hidpi_window
Storage for allow_hidpi setting. If true renders OTTD in native resolution.
NSString * OTTDMainLaunchGameEngine
Name of notification observer used to restart the game loop if necessary.
static bool _cocoa_video_dialog
True iff inside the scope of CocoaDialog method.
static NSImage * NSImageFromSprite(SpriteID sprite_id, ZoomLevel zoom)
Render an OTTD sprite to a Cocoa image.
bool _emulated_down
Whether the mouse button is emulated or real.
bool touchbar_created
Whether the touchbar exists.
static void setupWindowMenu()
Create a window menu.
void CocoaDialog(std::string_view title, std::string_view message, std::string_view buttonLabel)
Catch asserts prior to initialization of the videodriver.
bool _use_hidpi
Render content in native resolution?
static const std::array< TouchBarButton, 9 > _touchbar_buttons
Storage of defined touch bar buttons.
static NSUInteger CountUtf16Units(std::string_view str)
Count the number of UTF-16 code points in a range of an UTF-8 string.
static size_t Utf8AdvanceByUtf16Units(std::string_view str, NSUInteger count)
Advance an UTF-8 string by a number of equivalent UTF-16 code points.
static void CGDataFreeCallback(void *, const void *data, size_t)
Free memory where data was stored.
static void setApplicationMenu()
Initialize the application menu shown in top bar.
static std::vector< char32_t > NSStringToUTF32(NSString *s)
Convert a NSString to an UTF-32 encoded string.
NSUInteger _current_mods
Currently applied modifier flags.
static OTTDMain * _ottd_main
App delegate instance of OTTDMain.
bool CocoaSetupApplication()
Startup the application.
void CocoaExitApplication()
Deregister app delegate.
Functions related to debugging.
#define Debug(category, level, format_string,...)
Output a line of debugging information.
Dimension GetSpriteSize(SpriteID sprid, Point *offset, ZoomLevel zoom)
Get the size of a sprite.
bool _left_button_down
Is left mouse button pressed?
uint8_t _dirkeys
1 = left, 2 = up, 4 = right, 8 = down
bool _left_button_clicked
Is left mouse button clicked?
bool _right_button_clicked
Is right mouse button clicked?
std::unique_ptr< uint32_t[]> DrawSpriteToRgbaBuffer(SpriteID spriteId, ZoomLevel zoom)
Draws a sprite to a new RGBA buffer (see Colour union) instead of drawing to the screen.
bool _right_button_down
Is right mouse button pressed?
bool AdjustGUIZoom(bool automatic)
Resolve GUI zoom level and adjust GUI to new zoom, if auto-suggestion is requested.
Functions related to the gfx engine.
void HandleToolbarHotkey(int hotkey)
Handle Toolbar hotkey events - can come from a source like the MacBook Touch Bar.
void HandleMouseEvents()
Handle a mouse event from the video driver.
void HandleKeypress(uint keycode, char32_t key)
Handle keyboard input.
void HandleTextInput(std::string_view str, bool marked=false, std::optional< size_t > caret=std::nullopt, std::optional< size_t > insert_location=std::nullopt, std::optional< size_t > replacement_end=std::nullopt)
Handle text input.
uint32_t SpriteID
The number of a sprite, without mapping bits and colourtables.
The main class of the application, the application's delegate.
void unregisterObserver()
Remove ourself as a notification observer.
void stopEngine()
Stop the game engine.
Subclass of NSView to support mouse awareness and text input.
void unmarkText()
Unmark the current marked text.
NSRange selectedRange()
Get the caret position.
NSPoint mousePositionFromEvent:(NSEvent *e)
Return the mouse location.
void rightMouseDown:(NSEvent *event)
Handler of right mouse button pressing.
void setMarkedText:selectedRange:replacementRange:(id aString, [selectedRange] NSRange selRange, [replacementRange] NSRange replacementRange)
Set a new marked text and reposition the caret.
void internalMouseButtonEvent()
Internal handler of mouse buttons.
void moveToEndOfLine:(id sender)
Move cursor to the end of the line.
NSAttributedString * attributedString()
Get the current edit box string.
BOOL hasMarkedText()
Is any text marked?
void internalMouseMoveEvent:(NSEvent *event)
Internal handler of mouse movement.
BOOL internalHandleKeycode:unicode:pressed:modifiers:(unsigned short keycode, [unicode] char32_t unicode, [pressed] BOOL down, [modifiers] NSUInteger modifiers)
Internal handler of keyboard keys.
void moveToBeginningOfLine:(id sender)
Move cursor to the start of the line.
void rightMouseUp:(NSEvent *event)
Handler of right mouse button releasing.
BOOL acceptsFirstResponder()
Allow to handle events.
NSRect firstRectForCharacterRange:(NSRange aRange)
Get the bounding rect for the given range.
NSArray * validAttributesForMarkedText()
Get all string attributes that we can process for marked text.
NSRange markedRange()
Get the currently marked range.
void insertText:replacementRange:(id aString, [replacementRange] NSRange replacementRange)
Insert the given text at the given range.
NSAttributedString * attributedSubstringForProposedRange:actualRange:(NSRange theRange, [actualRange] NSRangePointer actualRange)
Get a string corresponding to the given range.
Delegate for our NSWindow to send ask for quit on close.
Subclass of NSWindow to cater our special needs.
Functions related to MacOS support.
std::unique_ptr< typename std::remove_pointer< T >::type, CFDeleter< typename std::remove_pointer< T >::type > > CFAutoRelease
Specialisation of std::unique_ptr for CoreFoundation objects.
Includes of mac os specific headers wich contain objective c.
#define Point
Macro that prevents name conflicts between included headers.
constexpr bool IsInsideMM(const size_t x, const size_t min, const size_t max) noexcept
Checks if a value is in an interval.
ClientSettings _settings_client
The current settings for this game.
Types related to global configuration settings.
Functions to cache sprites in memory.
This file contains all sprite-related enums and defines.
Definition of base types and functions in a cross-platform compatible way.
Functions related to low-level strings.
char32_t Utf16DecodeSurrogate(uint lead, uint trail)
Convert an UTF-16 surrogate pair to the corresponding Unicode character.
bool Utf16IsLeadSurrogate(uint c)
Is the given character a lead surrogate code point?
bool Utf16IsTrailSurrogate(uint c)
Is the given character a lead surrogate code point?
Dimensions (a width and height) of a rectangle in 2D.
Specification of a rectangle with absolute coordinates of all edges.
Helper/buffer for input fields.
uint16_t caretpos
the current position of the caret in the buffer, in bytes
std::string_view GetText() const
Get the current text.
uint16_t markend
the end position of the marked area in the buffer, in bytes
uint16_t markpos
the start position of the marked area in the buffer, in bytes
Stuff related to text buffers.
Handling of UTF-8 encoded data.
void ReInitAllWindows(bool zoom_changed)
Re-initialize all windows.
bool EditBoxInGlobalFocus()
Check if an edit box is in global focus.
Window functions not directly related to making/drawing windows.
Functions, definitions and such used only by the GUI.
ZoomLevel
All zoom levels we know.