|
@@ -91,6 +91,7 @@ NSMenu *DisplayServerOSX::_get_menu_root(const String &p_menu_root) {
|
|
|
// Submenu.
|
|
|
if (!submenu.has(p_menu_root)) {
|
|
|
NSMenu *n_menu = [[NSMenu alloc] initWithTitle:[NSString stringWithUTF8String:p_menu_root.utf8().get_data()]];
|
|
|
+ [n_menu setAutoenablesItems:NO];
|
|
|
submenu[p_menu_root] = n_menu;
|
|
|
}
|
|
|
menu = submenu[p_menu_root];
|
|
@@ -472,6 +473,40 @@ void DisplayServerOSX::_keyboard_layout_changed(CFNotificationCenterRef center,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+NSImage *DisplayServerOSX::_convert_to_nsimg(Ref<Image> &p_image) const {
|
|
|
+ p_image->convert(Image::FORMAT_RGBA8);
|
|
|
+ NSBitmapImageRep *imgrep = [[NSBitmapImageRep alloc]
|
|
|
+ initWithBitmapDataPlanes:NULL
|
|
|
+ pixelsWide:p_image->get_width()
|
|
|
+ pixelsHigh:p_image->get_height()
|
|
|
+ bitsPerSample:8
|
|
|
+ samplesPerPixel:4
|
|
|
+ hasAlpha:YES
|
|
|
+ isPlanar:NO
|
|
|
+ colorSpaceName:NSDeviceRGBColorSpace
|
|
|
+ bytesPerRow:int(p_image->get_width()) * 4
|
|
|
+ bitsPerPixel:32];
|
|
|
+ ERR_FAIL_COND_V(imgrep == nil, nil);
|
|
|
+ uint8_t *pixels = [imgrep bitmapData];
|
|
|
+
|
|
|
+ int len = p_image->get_width() * p_image->get_height();
|
|
|
+ const uint8_t *r = p_image->get_data().ptr();
|
|
|
+
|
|
|
+ /* Premultiply the alpha channel */
|
|
|
+ for (int i = 0; i < len; i++) {
|
|
|
+ uint8_t alpha = r[i * 4 + 3];
|
|
|
+ pixels[i * 4 + 0] = (uint8_t)(((uint16_t)r[i * 4 + 0] * alpha) / 255);
|
|
|
+ pixels[i * 4 + 1] = (uint8_t)(((uint16_t)r[i * 4 + 1] * alpha) / 255);
|
|
|
+ pixels[i * 4 + 2] = (uint8_t)(((uint16_t)r[i * 4 + 2] * alpha) / 255);
|
|
|
+ pixels[i * 4 + 3] = alpha;
|
|
|
+ }
|
|
|
+
|
|
|
+ NSImage *nsimg = [[NSImage alloc] initWithSize:NSMakeSize(p_image->get_width(), p_image->get_height())];
|
|
|
+ ERR_FAIL_COND_V(nsimg == nil, nil);
|
|
|
+ [nsimg addRepresentation:imgrep];
|
|
|
+ return nsimg;
|
|
|
+}
|
|
|
+
|
|
|
NSCursor *DisplayServerOSX::_cursor_from_selector(SEL p_selector, SEL p_fallback) {
|
|
|
if ([NSCursor respondsToSelector:p_selector]) {
|
|
|
id object = [NSCursor performSelector:p_selector];
|
|
@@ -498,7 +533,14 @@ void DisplayServerOSX::menu_callback(id p_sender) {
|
|
|
GodotMenuItem *value = [p_sender representedObject];
|
|
|
|
|
|
if (value) {
|
|
|
- if (value->checkable) {
|
|
|
+ if (value->max_states > 0) {
|
|
|
+ value->state++;
|
|
|
+ if (value->state >= value->max_states) {
|
|
|
+ value->state = 0;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (value->checkable_type == CHECKABLE_TYPE_CHECK_BOX) {
|
|
|
if ([p_sender state] == NSControlStateValueOff) {
|
|
|
[p_sender setState:NSControlStateValueOn];
|
|
|
} else {
|
|
@@ -671,35 +713,195 @@ String DisplayServerOSX::get_name() const {
|
|
|
return "OSX";
|
|
|
}
|
|
|
|
|
|
-void DisplayServerOSX::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
|
|
|
+void DisplayServerOSX::global_menu_add_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
|
|
|
+ NSMenuItem *menu_item;
|
|
|
+ if (p_index != -1) {
|
|
|
+ menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
|
|
|
+ } else {
|
|
|
+ menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
|
|
|
+ }
|
|
|
+ GodotMenuItem *obj = [[GodotMenuItem alloc] init];
|
|
|
+ obj->callback = p_callback;
|
|
|
+ obj->meta = p_tag;
|
|
|
+ obj->checkable_type = CHECKABLE_TYPE_NONE;
|
|
|
+ obj->max_states = 0;
|
|
|
+ obj->state = 0;
|
|
|
+ [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
|
|
|
+ [menu_item setRepresentedObject:obj];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
|
|
|
+ NSMenuItem *menu_item;
|
|
|
+ if (p_index != -1) {
|
|
|
+ menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
|
|
|
+ } else {
|
|
|
+ menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
|
|
|
+ }
|
|
|
+ GodotMenuItem *obj = [[GodotMenuItem alloc] init];
|
|
|
+ obj->callback = p_callback;
|
|
|
+ obj->meta = p_tag;
|
|
|
+ obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
|
|
|
+ obj->max_states = 0;
|
|
|
+ obj->state = 0;
|
|
|
+ [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
|
|
|
+ [menu_item setRepresentedObject:obj];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_add_icon_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
|
|
|
+ NSMenuItem *menu_item;
|
|
|
+ if (p_index != -1) {
|
|
|
+ menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
|
|
|
+ } else {
|
|
|
+ menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
|
|
|
+ }
|
|
|
+ GodotMenuItem *obj = [[GodotMenuItem alloc] init];
|
|
|
+ obj->callback = p_callback;
|
|
|
+ obj->meta = p_tag;
|
|
|
+ obj->checkable_type = CHECKABLE_TYPE_NONE;
|
|
|
+ obj->max_states = 0;
|
|
|
+ obj->state = 0;
|
|
|
+ if (p_icon.is_valid()) {
|
|
|
+ obj->img = p_icon->get_image();
|
|
|
+ obj->img = obj->img->duplicate();
|
|
|
+ if (obj->img->is_compressed()) {
|
|
|
+ obj->img->decompress();
|
|
|
+ }
|
|
|
+ obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS);
|
|
|
+ [menu_item setImage:_convert_to_nsimg(obj->img)];
|
|
|
+ }
|
|
|
+ [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
|
|
|
+ [menu_item setRepresentedObject:obj];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_add_icon_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
|
|
|
+ NSMenuItem *menu_item;
|
|
|
+ if (p_index != -1) {
|
|
|
+ menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
|
|
|
+ } else {
|
|
|
+ menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
|
|
|
+ }
|
|
|
+ GodotMenuItem *obj = [[GodotMenuItem alloc] init];
|
|
|
+ obj->callback = p_callback;
|
|
|
+ obj->meta = p_tag;
|
|
|
+ obj->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
|
|
|
+ obj->max_states = 0;
|
|
|
+ obj->state = 0;
|
|
|
+ if (p_icon.is_valid()) {
|
|
|
+ obj->img = p_icon->get_image();
|
|
|
+ obj->img = obj->img->duplicate();
|
|
|
+ if (obj->img->is_compressed()) {
|
|
|
+ obj->img->decompress();
|
|
|
+ }
|
|
|
+ obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS);
|
|
|
+ [menu_item setImage:_convert_to_nsimg(obj->img)];
|
|
|
+ }
|
|
|
+ [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
|
|
|
+ [menu_item setRepresentedObject:obj];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_add_radio_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
|
|
|
+ NSMenuItem *menu_item;
|
|
|
+ if (p_index != -1) {
|
|
|
+ menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
|
|
|
+ } else {
|
|
|
+ menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
|
|
|
+ }
|
|
|
+ GodotMenuItem *obj = [[GodotMenuItem alloc] init];
|
|
|
+ obj->callback = p_callback;
|
|
|
+ obj->meta = p_tag;
|
|
|
+ obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;
|
|
|
+ obj->max_states = 0;
|
|
|
+ obj->state = 0;
|
|
|
+ [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
|
|
|
+ [menu_item setRepresentedObject:obj];
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_add_icon_radio_check_item(const String &p_menu_root, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
|
NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
if (menu) {
|
|
|
- NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
|
|
|
+ String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
|
|
|
+ NSMenuItem *menu_item;
|
|
|
+ if (p_index != -1) {
|
|
|
+ menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
|
|
|
+ } else {
|
|
|
+ menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
|
|
|
+ }
|
|
|
GodotMenuItem *obj = [[GodotMenuItem alloc] init];
|
|
|
obj->callback = p_callback;
|
|
|
obj->meta = p_tag;
|
|
|
- obj->checkable = false;
|
|
|
+ obj->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;
|
|
|
+ obj->max_states = 0;
|
|
|
+ obj->state = 0;
|
|
|
+ if (p_icon.is_valid()) {
|
|
|
+ obj->img = p_icon->get_image();
|
|
|
+ obj->img = obj->img->duplicate();
|
|
|
+ if (obj->img->is_compressed()) {
|
|
|
+ obj->img->decompress();
|
|
|
+ }
|
|
|
+ obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS);
|
|
|
+ [menu_item setImage:_convert_to_nsimg(obj->img)];
|
|
|
+ }
|
|
|
+ [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
|
|
|
[menu_item setRepresentedObject:obj];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void DisplayServerOSX::global_menu_add_check_item(const String &p_menu_root, const String &p_label, const Callable &p_callback, const Variant &p_tag) {
|
|
|
+void DisplayServerOSX::global_menu_add_multistate_item(const String &p_menu_root, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Variant &p_tag, Key p_accel, int p_index) {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
|
NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
if (menu) {
|
|
|
- NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:@""];
|
|
|
+ String keycode = KeyMappingOSX::keycode_get_native_string(p_accel & KeyModifierMask::CODE_MASK);
|
|
|
+ NSMenuItem *menu_item;
|
|
|
+ if (p_index != -1) {
|
|
|
+ menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()] atIndex:p_index];
|
|
|
+ } else {
|
|
|
+ menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:@selector(globalMenuCallback:) keyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
|
|
|
+ }
|
|
|
GodotMenuItem *obj = [[GodotMenuItem alloc] init];
|
|
|
obj->callback = p_callback;
|
|
|
obj->meta = p_tag;
|
|
|
- obj->checkable = true;
|
|
|
+ obj->checkable_type = CHECKABLE_TYPE_NONE;
|
|
|
+ obj->max_states = p_max_states;
|
|
|
+ obj->state = p_default_state;
|
|
|
+ [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_accel)];
|
|
|
[menu_item setRepresentedObject:obj];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu) {
|
|
|
+void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, const String &p_label, const String &p_submenu, int p_index) {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
|
NSMenu *menu = _get_menu_root(p_menu_root);
|
|
@@ -713,18 +915,58 @@ void DisplayServerOSX::global_menu_add_submenu_item(const String &p_menu_root, c
|
|
|
ERR_PRINT("Can't set submenu to menu that is already a submenu of some other menu!");
|
|
|
return;
|
|
|
}
|
|
|
- NSMenuItem *menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@""];
|
|
|
+ NSMenuItem *menu_item;
|
|
|
+ if (p_index != -1) {
|
|
|
+ menu_item = [menu insertItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@"" atIndex:p_index];
|
|
|
+ } else {
|
|
|
+ menu_item = [menu addItemWithTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()] action:nil keyEquivalent:@""];
|
|
|
+ }
|
|
|
+ [sub_menu setTitle:[NSString stringWithUTF8String:p_label.utf8().get_data()]];
|
|
|
[menu setSubmenu:sub_menu forItem:menu_item];
|
|
|
}
|
|
|
}
|
|
|
|
|
|
-void DisplayServerOSX::global_menu_add_separator(const String &p_menu_root) {
|
|
|
+void DisplayServerOSX::global_menu_add_separator(const String &p_menu_root, int p_index) {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
|
NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
if (menu) {
|
|
|
- [menu addItem:[NSMenuItem separatorItem]];
|
|
|
+ if (p_index != -1) {
|
|
|
+ [menu insertItem:[NSMenuItem separatorItem] atIndex:p_index];
|
|
|
+ } else {
|
|
|
+ [menu addItem:[NSMenuItem separatorItem]];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+int DisplayServerOSX::global_menu_get_item_index_from_text(const String &p_menu_root, const String &p_text) const {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ return [menu indexOfItemWithTitle:[NSString stringWithUTF8String:p_text.utf8().get_data()]];
|
|
|
}
|
|
|
+
|
|
|
+ return -1;
|
|
|
+}
|
|
|
+
|
|
|
+int DisplayServerOSX::global_menu_get_item_index_from_tag(const String &p_menu_root, const Variant &p_tag) const {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ for (NSInteger i = 0; i < [menu numberOfItems]; i++) {
|
|
|
+ const NSMenuItem *menu_item = [menu itemAtIndex:i];
|
|
|
+ if (menu_item) {
|
|
|
+ const GodotMenuItem *obj = [menu_item representedObject];
|
|
|
+ if (obj && obj->meta == p_tag) {
|
|
|
+ return i;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ return -1;
|
|
|
}
|
|
|
|
|
|
bool DisplayServerOSX::global_menu_is_item_checked(const String &p_menu_root, int p_idx) const {
|
|
@@ -749,14 +991,30 @@ bool DisplayServerOSX::global_menu_is_item_checkable(const String &p_menu_root,
|
|
|
if (menu_item) {
|
|
|
GodotMenuItem *obj = [menu_item representedObject];
|
|
|
if (obj) {
|
|
|
- return obj->checkable;
|
|
|
+ return obj->checkable_type == CHECKABLE_TYPE_CHECK_BOX;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+bool DisplayServerOSX::global_menu_is_item_radio_checkable(const String &p_menu_root, int p_idx) const {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ GodotMenuItem *obj = [menu_item representedObject];
|
|
|
+ if (obj) {
|
|
|
+ return obj->checkable_type == CHECKABLE_TYPE_RADIO_BUTTON;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_root, int p_idx) {
|
|
|
+Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_root, int p_idx) const {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
|
const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
@@ -772,7 +1030,7 @@ Callable DisplayServerOSX::global_menu_get_item_callback(const String &p_menu_ro
|
|
|
return Callable();
|
|
|
}
|
|
|
|
|
|
-Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, int p_idx) {
|
|
|
+Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, int p_idx) const {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
|
const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
@@ -788,22 +1046,20 @@ Variant DisplayServerOSX::global_menu_get_item_tag(const String &p_menu_root, in
|
|
|
return Variant();
|
|
|
}
|
|
|
|
|
|
-String DisplayServerOSX::global_menu_get_item_text(const String &p_menu_root, int p_idx) {
|
|
|
+String DisplayServerOSX::global_menu_get_item_text(const String &p_menu_root, int p_idx) const {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
|
const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
if (menu) {
|
|
|
const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
if (menu_item) {
|
|
|
- String ret;
|
|
|
- ret.parse_utf8([[menu_item title] UTF8String]);
|
|
|
- return ret;
|
|
|
+ return String::utf8([[menu_item title] UTF8String]);
|
|
|
}
|
|
|
}
|
|
|
return String();
|
|
|
}
|
|
|
|
|
|
-String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root, int p_idx) {
|
|
|
+String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root, int p_idx) const {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
|
const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
@@ -823,6 +1079,116 @@ String DisplayServerOSX::global_menu_get_item_submenu(const String &p_menu_root,
|
|
|
return String();
|
|
|
}
|
|
|
|
|
|
+Key DisplayServerOSX::global_menu_get_item_accelerator(const String &p_menu_root, int p_idx) const {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ String ret = String::utf8([[menu_item keyEquivalent] UTF8String]);
|
|
|
+ Key keycode = find_keycode(ret);
|
|
|
+ NSUInteger mask = [menu_item keyEquivalentModifierMask];
|
|
|
+ if (mask & NSEventModifierFlagControl) {
|
|
|
+ keycode |= KeyModifierMask::CTRL;
|
|
|
+ }
|
|
|
+ if (mask & NSEventModifierFlagOption) {
|
|
|
+ keycode |= KeyModifierMask::ALT;
|
|
|
+ }
|
|
|
+ if (mask & NSEventModifierFlagShift) {
|
|
|
+ keycode |= KeyModifierMask::SHIFT;
|
|
|
+ }
|
|
|
+ if (mask & NSEventModifierFlagCommand) {
|
|
|
+ keycode |= KeyModifierMask::META;
|
|
|
+ }
|
|
|
+ if (mask & NSEventModifierFlagNumericPad) {
|
|
|
+ keycode |= KeyModifierMask::KPAD;
|
|
|
+ }
|
|
|
+ return keycode;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Key::NONE;
|
|
|
+}
|
|
|
+
|
|
|
+bool DisplayServerOSX::global_menu_is_item_disabled(const String &p_menu_root, int p_idx) const {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ return ![menu_item isEnabled];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return false;
|
|
|
+}
|
|
|
+
|
|
|
+String DisplayServerOSX::global_menu_get_item_tooltip(const String &p_menu_root, int p_idx) const {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ return String::utf8([[menu_item toolTip] UTF8String]);
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return String();
|
|
|
+}
|
|
|
+
|
|
|
+int DisplayServerOSX::global_menu_get_item_state(const String &p_menu_root, int p_idx) const {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ GodotMenuItem *obj = [menu_item representedObject];
|
|
|
+ if (obj) {
|
|
|
+ return obj->state;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+int DisplayServerOSX::global_menu_get_item_max_states(const String &p_menu_root, int p_idx) const {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ GodotMenuItem *obj = [menu_item representedObject];
|
|
|
+ if (obj) {
|
|
|
+ return obj->max_states;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+Ref<Texture2D> DisplayServerOSX::global_menu_get_item_icon(const String &p_menu_root, int p_idx) const {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ const NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ const NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ GodotMenuItem *obj = [menu_item representedObject];
|
|
|
+ if (obj) {
|
|
|
+ if (obj->img.is_valid()) {
|
|
|
+ Ref<ImageTexture> txt;
|
|
|
+ txt.instantiate();
|
|
|
+ txt->create_from_image(obj->img);
|
|
|
+ return txt;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return Ref<Texture2D>();
|
|
|
+}
|
|
|
+
|
|
|
void DisplayServerOSX::global_menu_set_item_checked(const String &p_menu_root, int p_idx, bool p_checked) {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
@@ -853,7 +1219,23 @@ void DisplayServerOSX::global_menu_set_item_checkable(const String &p_menu_root,
|
|
|
NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
if (menu_item) {
|
|
|
GodotMenuItem *obj = [menu_item representedObject];
|
|
|
- obj->checkable = p_checkable;
|
|
|
+ obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE;
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_set_item_radio_checkable(const String &p_menu_root, int p_idx, bool p_checkable) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ GodotMenuItem *obj = [menu_item representedObject];
|
|
|
+ obj->checkable_type = (p_checkable) ? CHECKABLE_TYPE_RADIO_BUTTON : CHECKABLE_TYPE_NONE;
|
|
|
}
|
|
|
}
|
|
|
}
|
|
@@ -929,6 +1311,116 @@ void DisplayServerOSX::global_menu_set_item_submenu(const String &p_menu_root, i
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+void DisplayServerOSX::global_menu_set_item_accelerator(const String &p_menu_root, int p_idx, Key p_keycode) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ [menu_item setKeyEquivalentModifierMask:KeyMappingOSX::keycode_get_native_mask(p_keycode)];
|
|
|
+ String keycode = KeyMappingOSX::keycode_get_native_string(p_keycode & KeyModifierMask::CODE_MASK);
|
|
|
+ [menu_item setKeyEquivalent:[NSString stringWithUTF8String:keycode.utf8().get_data()]];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_set_item_disabled(const String &p_menu_root, int p_idx, bool p_disabled) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ [menu_item setEnabled:(!p_disabled)];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_set_item_tooltip(const String &p_menu_root, int p_idx, const String &p_tooltip) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ [menu_item setToolTip:[NSString stringWithUTF8String:p_tooltip.utf8().get_data()]];
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_set_item_state(const String &p_menu_root, int p_idx, int p_state) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ GodotMenuItem *obj = [menu_item representedObject];
|
|
|
+ if (obj) {
|
|
|
+ obj->state = p_state;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_set_item_max_states(const String &p_menu_root, int p_idx, int p_max_states) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ GodotMenuItem *obj = [menu_item representedObject];
|
|
|
+ if (obj) {
|
|
|
+ obj->max_states = p_max_states;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
+void DisplayServerOSX::global_menu_set_item_icon(const String &p_menu_root, int p_idx, const Ref<Texture2D> &p_icon) {
|
|
|
+ _THREAD_SAFE_METHOD_
|
|
|
+
|
|
|
+ NSMenu *menu = _get_menu_root(p_menu_root);
|
|
|
+ if (menu) {
|
|
|
+ if ((menu == [NSApp mainMenu]) && (p_idx == 0)) { // Do not edit Apple menu.
|
|
|
+ return;
|
|
|
+ }
|
|
|
+ NSMenuItem *menu_item = [menu itemAtIndex:p_idx];
|
|
|
+ if (menu_item) {
|
|
|
+ GodotMenuItem *obj = [menu_item representedObject];
|
|
|
+ if (p_icon.is_valid()) {
|
|
|
+ obj->img = p_icon->get_image();
|
|
|
+ obj->img = obj->img->duplicate();
|
|
|
+ if (obj->img->is_compressed()) {
|
|
|
+ obj->img->decompress();
|
|
|
+ }
|
|
|
+ obj->img->resize(16, 16, Image::INTERPOLATE_LANCZOS);
|
|
|
+ [menu_item setImage:_convert_to_nsimg(obj->img)];
|
|
|
+ } else {
|
|
|
+ obj->img = Ref<Image>();
|
|
|
+ [menu_item setImage:nil];
|
|
|
+ }
|
|
|
+ }
|
|
|
+ }
|
|
|
+}
|
|
|
+
|
|
|
int DisplayServerOSX::global_menu_get_item_count(const String &p_menu_root) const {
|
|
|
_THREAD_SAFE_METHOD_
|
|
|
|
|
@@ -2628,11 +3120,13 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
|
|
|
|
|
|
// Setup Dock menu.
|
|
|
dock_menu = [[NSMenu alloc] initWithTitle:@"_dock"];
|
|
|
+ [dock_menu setAutoenablesItems:NO];
|
|
|
|
|
|
// Setup Apple menu.
|
|
|
apple_menu = [[NSMenu alloc] initWithTitle:@""];
|
|
|
title = [NSString stringWithFormat:NSLocalizedString(@"About %@", nil), nsappname];
|
|
|
[apple_menu addItemWithTitle:title action:@selector(showAbout:) keyEquivalent:@""];
|
|
|
+ [apple_menu setAutoenablesItems:NO];
|
|
|
|
|
|
[apple_menu addItem:[NSMenuItem separatorItem]];
|
|
|
|
|
@@ -2660,6 +3154,7 @@ DisplayServerOSX::DisplayServerOSX(const String &p_rendering_driver, WindowMode
|
|
|
NSMenu *main_menu = [NSApp mainMenu];
|
|
|
menu_item = [main_menu addItemWithTitle:@"" action:nil keyEquivalent:@""];
|
|
|
[main_menu setSubmenu:apple_menu forItem:menu_item];
|
|
|
+ [main_menu setAutoenablesItems:NO];
|
|
|
|
|
|
//!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
|
//TODO - do Vulkan and OpenGL support checks, driver selection and fallback
|