Path: blob/master/platform/windows/native_menu_windows.cpp
10277 views
/**************************************************************************/1/* native_menu_windows.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "native_menu_windows.h"3132#include "display_server_windows.h"3334#include "scene/resources/image_texture.h"3536HBITMAP NativeMenuWindows::_make_bitmap(const Ref<Image> &p_img) const {37p_img->convert(Image::FORMAT_RGBA8);3839Vector2i texture_size = p_img->get_size();40UINT image_size = texture_size.width * texture_size.height;4142COLORREF *buffer = nullptr;4344BITMAPV5HEADER bi;45ZeroMemory(&bi, sizeof(bi));46bi.bV5Size = sizeof(bi);47bi.bV5Width = texture_size.width;48bi.bV5Height = -texture_size.height;49bi.bV5Planes = 1;50bi.bV5BitCount = 32;51bi.bV5Compression = BI_BITFIELDS;52bi.bV5RedMask = 0x00ff0000;53bi.bV5GreenMask = 0x0000ff00;54bi.bV5BlueMask = 0x000000ff;55bi.bV5AlphaMask = 0xff000000;5657HDC dc = GetDC(nullptr);58HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0);59for (UINT index = 0; index < image_size; index++) {60int row_index = std::floor(index / texture_size.width);61int column_index = (index % int(texture_size.width));62const Color &c = p_img->get_pixel(column_index, row_index);63*(buffer + index) = c.to_argb32();64}65ReleaseDC(nullptr, dc);6667return bitmap;68}6970void NativeMenuWindows::_menu_activate(HMENU p_menu, int p_index) const {71if (menu_lookup.has(p_menu)) {72MenuData *md = menus.get_or_null(menu_lookup[p_menu]);73if (md) {74int count = GetMenuItemCount(md->menu);75if (p_index >= 0 && p_index < count) {76MENUITEMINFOW item;77ZeroMemory(&item, sizeof(item));78item.cbSize = sizeof(item);79item.fMask = MIIM_STATE | MIIM_DATA;80if (GetMenuItemInfoW(md->menu, p_index, true, &item)) {81MenuItemData *item_data = (MenuItemData *)item.dwItemData;82if (item_data) {83if (item_data->callback.is_valid()) {84Variant ret;85Callable::CallError ce;86const Variant *args[1] = { &item_data->meta };8788item_data->callback.callp(args, 1, ret, ce);89if (ce.error != Callable::CallError::CALL_OK) {90ERR_PRINT(vformat("Failed to execute menu callback: %s.", Variant::get_callable_error_text(item_data->callback, args, 1, ce)));91}92}93}94}95}96}97}98}99100bool NativeMenuWindows::has_feature(Feature p_feature) const {101switch (p_feature) {102// case FEATURE_GLOBAL_MENU:103// case FEATURE_OPEN_CLOSE_CALLBACK:104// case FEATURE_HOVER_CALLBACK:105// case FEATURE_KEY_CALLBACK:106case FEATURE_POPUP_MENU:107return true;108default:109return false;110}111}112113bool NativeMenuWindows::has_system_menu(SystemMenus p_menu_id) const {114return false;115}116117RID NativeMenuWindows::get_system_menu(SystemMenus p_menu_id) const {118return RID();119}120121RID NativeMenuWindows::create_menu() {122MenuData *md = memnew(MenuData);123md->menu = CreatePopupMenu();124125MENUINFO menu_info;126ZeroMemory(&menu_info, sizeof(menu_info));127menu_info.cbSize = sizeof(menu_info);128menu_info.fMask = MIM_STYLE;129menu_info.dwStyle = MNS_NOTIFYBYPOS;130SetMenuInfo(md->menu, &menu_info);131132RID rid = menus.make_rid(md);133menu_lookup[md->menu] = rid;134return rid;135}136137bool NativeMenuWindows::has_menu(const RID &p_rid) const {138return menus.owns(p_rid);139}140141void NativeMenuWindows::free_menu(const RID &p_rid) {142MenuData *md = menus.get_or_null(p_rid);143if (md) {144clear(p_rid);145DestroyMenu(md->menu);146menus.free(p_rid);147menu_lookup.erase(md->menu);148memdelete(md);149}150}151152Size2 NativeMenuWindows::get_size(const RID &p_rid) const {153const MenuData *md = menus.get_or_null(p_rid);154ERR_FAIL_NULL_V(md, Size2());155156Size2 size;157int count = GetMenuItemCount(md->menu);158for (int i = 0; i < count; i++) {159RECT rect;160if (GetMenuItemRect(nullptr, md->menu, i, &rect)) {161size.x = MAX(size.x, rect.right - rect.left);162size.y += rect.bottom - rect.top;163}164}165return size;166}167168void NativeMenuWindows::popup(const RID &p_rid, const Vector2i &p_position) {169const MenuData *md = menus.get_or_null(p_rid);170ERR_FAIL_NULL(md);171172HWND hwnd = (HWND)DisplayServer::get_singleton()->window_get_native_handle(DisplayServer::WINDOW_HANDLE, DisplayServer::MAIN_WINDOW_ID);173UINT flags = TPM_HORIZONTAL | TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_VERPOSANIMATION;174if (md->is_rtl) {175flags |= TPM_LAYOUTRTL;176}177SetForegroundWindow(hwnd);178TrackPopupMenuEx(md->menu, flags, p_position.x, p_position.y, hwnd, nullptr);179180if (md->close_cb.is_valid()) {181Variant ret;182Callable::CallError ce;183md->close_cb.callp(nullptr, 0, ret, ce);184if (ce.error != Callable::CallError::CALL_OK) {185ERR_PRINT(vformat("Failed to execute popup close callback: %s.", Variant::get_callable_error_text(md->close_cb, nullptr, 0, ce)));186}187}188189PostMessage(hwnd, WM_NULL, 0, 0);190}191192void NativeMenuWindows::set_interface_direction(const RID &p_rid, bool p_is_rtl) {193MenuData *md = menus.get_or_null(p_rid);194ERR_FAIL_NULL(md);195196if (md->is_rtl == p_is_rtl) {197return;198}199md->is_rtl = p_is_rtl;200}201202void NativeMenuWindows::set_popup_open_callback(const RID &p_rid, const Callable &p_callback) {203// Not supported.204}205206Callable NativeMenuWindows::get_popup_open_callback(const RID &p_rid) const {207// Not supported.208return Callable();209}210211void NativeMenuWindows::set_popup_close_callback(const RID &p_rid, const Callable &p_callback) {212MenuData *md = menus.get_or_null(p_rid);213ERR_FAIL_NULL(md);214215md->close_cb = p_callback;216}217218Callable NativeMenuWindows::get_popup_close_callback(const RID &p_rid) const {219const MenuData *md = menus.get_or_null(p_rid);220ERR_FAIL_NULL_V(md, Callable());221222return md->close_cb;223}224225void NativeMenuWindows::set_minimum_width(const RID &p_rid, float p_width) {226// Not supported.227}228229float NativeMenuWindows::get_minimum_width(const RID &p_rid) const {230// Not supported.231return 0.f;232}233234bool NativeMenuWindows::is_opened(const RID &p_rid) const {235// Not supported.236return false;237}238239int NativeMenuWindows::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) {240MenuData *md = menus.get_or_null(p_rid);241MenuData *md_sub = menus.get_or_null(p_submenu_rid);242ERR_FAIL_NULL_V(md, -1);243ERR_FAIL_NULL_V(md_sub, -1);244ERR_FAIL_COND_V_MSG(md->menu == md_sub->menu, -1, "Can't set submenu to self!");245246if (p_index == -1) {247p_index = GetMenuItemCount(md->menu);248} else {249p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));250}251252MenuItemData *item_data = memnew(MenuItemData);253item_data->meta = p_tag;254item_data->checkable_type = CHECKABLE_TYPE_NONE;255item_data->max_states = 0;256item_data->state = 0;257258Char16String label = p_label.utf16();259MENUITEMINFOW item;260ZeroMemory(&item, sizeof(item));261item.cbSize = sizeof(item);262item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA | MIIM_SUBMENU;263item.fType = MFT_STRING;264item.hSubMenu = md_sub->menu;265item.dwItemData = (ULONG_PTR)item_data;266item.dwTypeData = (LPWSTR)label.get_data();267268if (!InsertMenuItemW(md->menu, p_index, true, &item)) {269memdelete(item_data);270return -1;271}272return p_index;273}274275int NativeMenuWindows::add_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {276const MenuData *md = menus.get_or_null(p_rid);277ERR_FAIL_NULL_V(md, -1);278279if (p_index == -1) {280p_index = GetMenuItemCount(md->menu);281} else {282p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));283}284285MenuItemData *item_data = memnew(MenuItemData);286item_data->callback = p_callback;287item_data->meta = p_tag;288item_data->checkable_type = CHECKABLE_TYPE_NONE;289item_data->max_states = 0;290item_data->state = 0;291292Char16String label = p_label.utf16();293MENUITEMINFOW item;294ZeroMemory(&item, sizeof(item));295item.cbSize = sizeof(item);296item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA;297item.fType = MFT_STRING;298item.dwItemData = (ULONG_PTR)item_data;299item.dwTypeData = (LPWSTR)label.get_data();300301if (!InsertMenuItemW(md->menu, p_index, true, &item)) {302memdelete(item_data);303return -1;304}305return p_index;306}307308int NativeMenuWindows::add_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {309const MenuData *md = menus.get_or_null(p_rid);310ERR_FAIL_NULL_V(md, -1);311312if (p_index == -1) {313p_index = GetMenuItemCount(md->menu);314} else {315p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));316}317318MenuItemData *item_data = memnew(MenuItemData);319item_data->callback = p_callback;320item_data->meta = p_tag;321item_data->checkable_type = CHECKABLE_TYPE_CHECK_BOX;322item_data->max_states = 0;323item_data->state = 0;324325Char16String label = p_label.utf16();326MENUITEMINFOW item;327ZeroMemory(&item, sizeof(item));328item.cbSize = sizeof(item);329item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA;330item.fType = MFT_STRING;331item.dwItemData = (ULONG_PTR)item_data;332item.dwTypeData = (LPWSTR)label.get_data();333334if (!InsertMenuItemW(md->menu, p_index, true, &item)) {335memdelete(item_data);336return -1;337}338return p_index;339}340341int NativeMenuWindows::add_icon_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {342const MenuData *md = menus.get_or_null(p_rid);343ERR_FAIL_NULL_V(md, -1);344345if (p_index == -1) {346p_index = GetMenuItemCount(md->menu);347} else {348p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));349}350351MenuItemData *item_data = memnew(MenuItemData);352item_data->callback = p_callback;353item_data->meta = p_tag;354item_data->checkable_type = CHECKABLE_TYPE_NONE;355item_data->max_states = 0;356item_data->state = 0;357if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {358item_data->img = p_icon->get_image();359item_data->img = item_data->img->duplicate();360if (item_data->img->is_compressed()) {361item_data->img->decompress();362}363item_data->bmp = _make_bitmap(item_data->img);364}365366Char16String label = p_label.utf16();367MENUITEMINFOW item;368ZeroMemory(&item, sizeof(item));369item.cbSize = sizeof(item);370item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA | MIIM_BITMAP;371item.fType = MFT_STRING;372item.dwItemData = (ULONG_PTR)item_data;373item.dwTypeData = (LPWSTR)label.get_data();374item.hbmpItem = item_data->bmp;375376if (!InsertMenuItemW(md->menu, p_index, true, &item)) {377memdelete(item_data);378return -1;379}380return p_index;381}382383int NativeMenuWindows::add_icon_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {384const MenuData *md = menus.get_or_null(p_rid);385ERR_FAIL_NULL_V(md, -1);386387if (p_index == -1) {388p_index = GetMenuItemCount(md->menu);389} else {390p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));391}392393MenuItemData *item_data = memnew(MenuItemData);394item_data->callback = p_callback;395item_data->meta = p_tag;396item_data->checkable_type = CHECKABLE_TYPE_CHECK_BOX;397item_data->max_states = 0;398item_data->state = 0;399if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {400item_data->img = p_icon->get_image();401item_data->img = item_data->img->duplicate();402if (item_data->img->is_compressed()) {403item_data->img->decompress();404}405item_data->bmp = _make_bitmap(item_data->img);406}407408Char16String label = p_label.utf16();409MENUITEMINFOW item;410ZeroMemory(&item, sizeof(item));411item.cbSize = sizeof(item);412item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA | MIIM_BITMAP;413item.fType = MFT_STRING;414item.dwItemData = (ULONG_PTR)item_data;415item.dwTypeData = (LPWSTR)label.get_data();416item.hbmpItem = item_data->bmp;417418if (!InsertMenuItemW(md->menu, p_index, true, &item)) {419memdelete(item_data);420return -1;421}422return p_index;423}424425int NativeMenuWindows::add_radio_check_item(const RID &p_rid, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {426const MenuData *md = menus.get_or_null(p_rid);427ERR_FAIL_NULL_V(md, -1);428429if (p_index == -1) {430p_index = GetMenuItemCount(md->menu);431} else {432p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));433}434435MenuItemData *item_data = memnew(MenuItemData);436item_data->callback = p_callback;437item_data->meta = p_tag;438item_data->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;439item_data->max_states = 0;440item_data->state = 0;441442Char16String label = p_label.utf16();443MENUITEMINFOW item;444ZeroMemory(&item, sizeof(item));445item.cbSize = sizeof(item);446item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA;447item.fType = MFT_STRING | MFT_RADIOCHECK;448item.dwItemData = (ULONG_PTR)item_data;449item.dwTypeData = (LPWSTR)label.get_data();450451if (!InsertMenuItemW(md->menu, p_index, true, &item)) {452memdelete(item_data);453return -1;454}455return p_index;456}457458int NativeMenuWindows::add_icon_radio_check_item(const RID &p_rid, const Ref<Texture2D> &p_icon, const String &p_label, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {459const MenuData *md = menus.get_or_null(p_rid);460ERR_FAIL_NULL_V(md, -1);461462if (p_index == -1) {463p_index = GetMenuItemCount(md->menu);464} else {465p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));466}467468MenuItemData *item_data = memnew(MenuItemData);469item_data->callback = p_callback;470item_data->meta = p_tag;471item_data->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;472item_data->max_states = 0;473item_data->state = 0;474if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {475item_data->img = p_icon->get_image();476item_data->img = item_data->img->duplicate();477if (item_data->img->is_compressed()) {478item_data->img->decompress();479}480item_data->bmp = _make_bitmap(item_data->img);481}482483Char16String label = p_label.utf16();484MENUITEMINFOW item;485ZeroMemory(&item, sizeof(item));486item.cbSize = sizeof(item);487item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA | MIIM_BITMAP;488item.fType = MFT_STRING | MFT_RADIOCHECK;489item.dwItemData = (ULONG_PTR)item_data;490item.dwTypeData = (LPWSTR)label.get_data();491item.hbmpItem = item_data->bmp;492493if (!InsertMenuItemW(md->menu, p_index, true, &item)) {494memdelete(item_data);495return -1;496}497return p_index;498}499500int NativeMenuWindows::add_multistate_item(const RID &p_rid, const String &p_label, int p_max_states, int p_default_state, const Callable &p_callback, const Callable &p_key_callback, const Variant &p_tag, Key p_accel, int p_index) {501const MenuData *md = menus.get_or_null(p_rid);502ERR_FAIL_NULL_V(md, -1);503504if (p_index == -1) {505p_index = GetMenuItemCount(md->menu);506} else {507p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));508}509510MenuItemData *item_data = memnew(MenuItemData);511item_data->callback = p_callback;512item_data->meta = p_tag;513item_data->checkable_type = CHECKABLE_TYPE_NONE;514item_data->max_states = p_max_states;515item_data->state = p_default_state;516517Char16String label = p_label.utf16();518MENUITEMINFOW item;519ZeroMemory(&item, sizeof(item));520item.cbSize = sizeof(item);521item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA;522item.fType = MFT_STRING;523item.dwItemData = (ULONG_PTR)item_data;524item.dwTypeData = (LPWSTR)label.get_data();525526if (!InsertMenuItemW(md->menu, p_index, true, &item)) {527memdelete(item_data);528return -1;529}530return p_index;531}532533int NativeMenuWindows::add_separator(const RID &p_rid, int p_index) {534const MenuData *md = menus.get_or_null(p_rid);535ERR_FAIL_NULL_V(md, -1);536537if (p_index == -1) {538p_index = GetMenuItemCount(md->menu);539} else {540p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));541}542543MenuItemData *item_data = memnew(MenuItemData);544item_data->checkable_type = CHECKABLE_TYPE_NONE;545item_data->max_states = 0;546item_data->state = 0;547548MENUITEMINFOW item;549ZeroMemory(&item, sizeof(item));550item.cbSize = sizeof(item);551item.fMask = MIIM_FTYPE | MIIM_DATA;552item.fType = MFT_SEPARATOR;553item.dwItemData = (ULONG_PTR)item_data;554555if (!InsertMenuItemW(md->menu, p_index, true, &item)) {556memdelete(item_data);557return -1;558}559return p_index;560}561562int NativeMenuWindows::find_item_index_with_text(const RID &p_rid, const String &p_text) const {563const MenuData *md = menus.get_or_null(p_rid);564ERR_FAIL_NULL_V(md, -1);565566MENUITEMINFOW item;567int count = GetMenuItemCount(md->menu);568for (int i = 0; i < count; i++) {569ZeroMemory(&item, sizeof(item));570item.cbSize = sizeof(item);571item.fMask = MIIM_STRING;572item.dwTypeData = nullptr;573if (GetMenuItemInfoW(md->menu, i, true, &item)) {574item.cch++;575Char16String str;576str.resize_uninitialized(item.cch);577item.dwTypeData = (LPWSTR)str.ptrw();578if (GetMenuItemInfoW(md->menu, i, true, &item)) {579if (String::utf16((const char16_t *)str.get_data()) == p_text) {580return i;581}582}583}584}585return -1;586}587588int NativeMenuWindows::find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const {589const MenuData *md = menus.get_or_null(p_rid);590ERR_FAIL_NULL_V(md, -1);591592MENUITEMINFOW item;593int count = GetMenuItemCount(md->menu);594for (int i = 0; i < count; i++) {595ZeroMemory(&item, sizeof(item));596item.cbSize = sizeof(item);597item.fMask = MIIM_DATA;598if (GetMenuItemInfoW(md->menu, i, true, &item)) {599MenuItemData *item_data = (MenuItemData *)item.dwItemData;600if (item_data) {601if (item_data->meta == p_tag) {602return i;603}604}605}606}607return -1;608}609610bool NativeMenuWindows::is_item_checked(const RID &p_rid, int p_idx) const {611ERR_FAIL_COND_V(p_idx < 0, false);612const MenuData *md = menus.get_or_null(p_rid);613ERR_FAIL_NULL_V(md, false);614int count = GetMenuItemCount(md->menu);615ERR_FAIL_COND_V(p_idx >= count, false);616617MENUITEMINFOW item;618ZeroMemory(&item, sizeof(item));619item.cbSize = sizeof(item);620item.fMask = MIIM_STATE | MIIM_DATA;621if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {622MenuItemData *item_data = (MenuItemData *)item.dwItemData;623if (item_data) {624return item_data->checked;625}626}627return false;628}629630bool NativeMenuWindows::is_item_checkable(const RID &p_rid, int p_idx) const {631ERR_FAIL_COND_V(p_idx < 0, false);632const MenuData *md = menus.get_or_null(p_rid);633ERR_FAIL_NULL_V(md, false);634int count = GetMenuItemCount(md->menu);635ERR_FAIL_COND_V(p_idx >= count, false);636637MENUITEMINFOW item;638ZeroMemory(&item, sizeof(item));639item.cbSize = sizeof(item);640item.fMask = MIIM_DATA;641if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {642MenuItemData *item_data = (MenuItemData *)item.dwItemData;643if (item_data) {644return item_data->checkable_type == CHECKABLE_TYPE_CHECK_BOX;645}646}647return false;648}649650bool NativeMenuWindows::is_item_radio_checkable(const RID &p_rid, int p_idx) const {651ERR_FAIL_COND_V(p_idx < 0, false);652const MenuData *md = menus.get_or_null(p_rid);653ERR_FAIL_NULL_V(md, false);654int count = GetMenuItemCount(md->menu);655ERR_FAIL_COND_V(p_idx >= count, false);656657MENUITEMINFOW item;658ZeroMemory(&item, sizeof(item));659item.cbSize = sizeof(item);660item.fMask = MIIM_DATA;661if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {662MenuItemData *item_data = (MenuItemData *)item.dwItemData;663if (item_data) {664return item_data->checkable_type == CHECKABLE_TYPE_RADIO_BUTTON;665}666}667return false;668}669670Callable NativeMenuWindows::get_item_callback(const RID &p_rid, int p_idx) const {671ERR_FAIL_COND_V(p_idx < 0, Callable());672const MenuData *md = menus.get_or_null(p_rid);673ERR_FAIL_NULL_V(md, Callable());674int count = GetMenuItemCount(md->menu);675ERR_FAIL_COND_V(p_idx >= count, Callable());676677MENUITEMINFOW item;678ZeroMemory(&item, sizeof(item));679item.cbSize = sizeof(item);680item.fMask = MIIM_DATA;681if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {682MenuItemData *item_data = (MenuItemData *)item.dwItemData;683if (item_data) {684return item_data->callback;685}686}687return Callable();688}689690Callable NativeMenuWindows::get_item_key_callback(const RID &p_rid, int p_idx) const {691// Not supported.692return Callable();693}694695Variant NativeMenuWindows::get_item_tag(const RID &p_rid, int p_idx) const {696ERR_FAIL_COND_V(p_idx < 0, Variant());697const MenuData *md = menus.get_or_null(p_rid);698ERR_FAIL_NULL_V(md, Variant());699int count = GetMenuItemCount(md->menu);700ERR_FAIL_COND_V(p_idx >= count, Variant());701702MENUITEMINFOW item;703ZeroMemory(&item, sizeof(item));704item.cbSize = sizeof(item);705item.fMask = MIIM_DATA;706if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {707MenuItemData *item_data = (MenuItemData *)item.dwItemData;708if (item_data) {709return item_data->meta;710}711}712return Variant();713}714715String NativeMenuWindows::get_item_text(const RID &p_rid, int p_idx) const {716ERR_FAIL_COND_V(p_idx < 0, String());717const MenuData *md = menus.get_or_null(p_rid);718ERR_FAIL_NULL_V(md, String());719int count = GetMenuItemCount(md->menu);720ERR_FAIL_COND_V(p_idx >= count, String());721722MENUITEMINFOW item;723ZeroMemory(&item, sizeof(item));724item.cbSize = sizeof(item);725item.fMask = MIIM_STRING;726item.dwTypeData = nullptr;727if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {728item.cch++;729Char16String str;730str.resize_uninitialized(item.cch);731item.dwTypeData = (LPWSTR)str.ptrw();732if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {733return String::utf16((const char16_t *)str.get_data());734}735}736return String();737}738739RID NativeMenuWindows::get_item_submenu(const RID &p_rid, int p_idx) const {740ERR_FAIL_COND_V(p_idx < 0, RID());741const MenuData *md = menus.get_or_null(p_rid);742ERR_FAIL_NULL_V(md, RID());743int count = GetMenuItemCount(md->menu);744ERR_FAIL_COND_V(p_idx >= count, RID());745746MENUITEMINFOW item;747ZeroMemory(&item, sizeof(item));748item.cbSize = sizeof(item);749item.fMask = MIIM_SUBMENU;750if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {751if (menu_lookup.has(item.hSubMenu)) {752return menu_lookup[item.hSubMenu];753}754}755return RID();756}757758Key NativeMenuWindows::get_item_accelerator(const RID &p_rid, int p_idx) const {759// Not supported.760return Key::NONE;761}762763bool NativeMenuWindows::is_item_disabled(const RID &p_rid, int p_idx) const {764ERR_FAIL_COND_V(p_idx < 0, false);765const MenuData *md = menus.get_or_null(p_rid);766ERR_FAIL_NULL_V(md, false);767int count = GetMenuItemCount(md->menu);768ERR_FAIL_COND_V(p_idx >= count, false);769770MENUITEMINFOW item;771ZeroMemory(&item, sizeof(item));772item.cbSize = sizeof(item);773item.fMask = MIIM_STATE;774if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {775return (item.fState & MFS_DISABLED) == MFS_DISABLED;776}777return false;778}779780bool NativeMenuWindows::is_item_hidden(const RID &p_rid, int p_idx) const {781// Not supported.782return false;783}784785String NativeMenuWindows::get_item_tooltip(const RID &p_rid, int p_idx) const {786// Not supported.787return String();788}789790int NativeMenuWindows::get_item_state(const RID &p_rid, int p_idx) const {791ERR_FAIL_COND_V(p_idx < 0, -1);792const MenuData *md = menus.get_or_null(p_rid);793ERR_FAIL_NULL_V(md, -1);794int count = GetMenuItemCount(md->menu);795ERR_FAIL_COND_V(p_idx >= count, -1);796797MENUITEMINFOW item;798ZeroMemory(&item, sizeof(item));799item.cbSize = sizeof(item);800item.fMask = MIIM_DATA;801if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {802MenuItemData *item_data = (MenuItemData *)item.dwItemData;803if (item_data) {804return item_data->state;805}806}807return -1;808}809810int NativeMenuWindows::get_item_max_states(const RID &p_rid, int p_idx) const {811ERR_FAIL_COND_V(p_idx < 0, -1);812const MenuData *md = menus.get_or_null(p_rid);813ERR_FAIL_NULL_V(md, -1);814int count = GetMenuItemCount(md->menu);815ERR_FAIL_COND_V(p_idx >= count, -1);816817MENUITEMINFOW item;818ZeroMemory(&item, sizeof(item));819item.cbSize = sizeof(item);820item.fMask = MIIM_DATA;821if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {822MenuItemData *item_data = (MenuItemData *)item.dwItemData;823if (item_data) {824return item_data->max_states;825}826}827return -1;828}829830Ref<Texture2D> NativeMenuWindows::get_item_icon(const RID &p_rid, int p_idx) const {831ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>());832const MenuData *md = menus.get_or_null(p_rid);833ERR_FAIL_NULL_V(md, Ref<Texture2D>());834int count = GetMenuItemCount(md->menu);835ERR_FAIL_COND_V(p_idx >= count, Ref<Texture2D>());836837MENUITEMINFOW item;838ZeroMemory(&item, sizeof(item));839item.cbSize = sizeof(item);840item.fMask = MIIM_DATA;841if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {842MenuItemData *item_data = (MenuItemData *)item.dwItemData;843if (item_data) {844return ImageTexture::create_from_image(item_data->img);845}846}847return Ref<Texture2D>();848}849850int NativeMenuWindows::get_item_indentation_level(const RID &p_rid, int p_idx) const {851// Not supported.852return 0;853}854855void NativeMenuWindows::set_item_checked(const RID &p_rid, int p_idx, bool p_checked) {856ERR_FAIL_COND(p_idx < 0);857const MenuData *md = menus.get_or_null(p_rid);858ERR_FAIL_NULL(md);859int count = GetMenuItemCount(md->menu);860ERR_FAIL_COND(p_idx >= count);861862MENUITEMINFOW item;863ZeroMemory(&item, sizeof(item));864item.cbSize = sizeof(item);865item.fMask = MIIM_STATE | MIIM_DATA;866if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {867MenuItemData *item_data = (MenuItemData *)item.dwItemData;868if (item_data) {869item_data->checked = p_checked;870if (p_checked) {871item.fState |= MFS_CHECKED;872} else {873item.fState &= ~MFS_CHECKED;874}875}876SetMenuItemInfoW(md->menu, p_idx, true, &item);877}878}879880void NativeMenuWindows::set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable) {881ERR_FAIL_COND(p_idx < 0);882const MenuData *md = menus.get_or_null(p_rid);883ERR_FAIL_NULL(md);884int count = GetMenuItemCount(md->menu);885ERR_FAIL_COND(p_idx >= count);886887MENUITEMINFOW item;888ZeroMemory(&item, sizeof(item));889item.cbSize = sizeof(item);890item.fMask = MIIM_FTYPE | MIIM_DATA;891if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {892MenuItemData *item_data = (MenuItemData *)item.dwItemData;893if (item_data) {894item.fType &= ~MFT_RADIOCHECK;895item_data->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE;896SetMenuItemInfoW(md->menu, p_idx, true, &item);897}898}899}900901void NativeMenuWindows::set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable) {902ERR_FAIL_COND(p_idx < 0);903const MenuData *md = menus.get_or_null(p_rid);904ERR_FAIL_NULL(md);905int count = GetMenuItemCount(md->menu);906ERR_FAIL_COND(p_idx >= count);907908MENUITEMINFOW item;909ZeroMemory(&item, sizeof(item));910item.cbSize = sizeof(item);911item.fMask = MIIM_FTYPE | MIIM_DATA;912if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {913MenuItemData *item_data = (MenuItemData *)item.dwItemData;914if (item_data) {915if (p_checkable) {916item.fType |= MFT_RADIOCHECK;917item_data->checkable_type = CHECKABLE_TYPE_CHECK_BOX;918} else {919item.fType &= ~MFT_RADIOCHECK;920item_data->checkable_type = CHECKABLE_TYPE_NONE;921}922SetMenuItemInfoW(md->menu, p_idx, true, &item);923}924}925}926927void NativeMenuWindows::set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback) {928ERR_FAIL_COND(p_idx < 0);929const MenuData *md = menus.get_or_null(p_rid);930ERR_FAIL_NULL(md);931int count = GetMenuItemCount(md->menu);932ERR_FAIL_COND(p_idx >= count);933934MENUITEMINFOW item;935ZeroMemory(&item, sizeof(item));936item.cbSize = sizeof(item);937item.fMask = MIIM_DATA;938if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {939MenuItemData *item_data = (MenuItemData *)item.dwItemData;940if (item_data) {941item_data->callback = p_callback;942}943}944}945946void NativeMenuWindows::set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback) {947// Not supported.948}949950void NativeMenuWindows::set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback) {951// Not supported.952}953954void NativeMenuWindows::set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag) {955ERR_FAIL_COND(p_idx < 0);956const MenuData *md = menus.get_or_null(p_rid);957ERR_FAIL_NULL(md);958int count = GetMenuItemCount(md->menu);959ERR_FAIL_COND(p_idx >= count);960961MENUITEMINFOW item;962ZeroMemory(&item, sizeof(item));963item.cbSize = sizeof(item);964item.fMask = MIIM_DATA;965if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {966MenuItemData *item_data = (MenuItemData *)item.dwItemData;967if (item_data) {968item_data->meta = p_tag;969}970}971}972973void NativeMenuWindows::set_item_text(const RID &p_rid, int p_idx, const String &p_text) {974ERR_FAIL_COND(p_idx < 0);975const MenuData *md = menus.get_or_null(p_rid);976ERR_FAIL_NULL(md);977int count = GetMenuItemCount(md->menu);978ERR_FAIL_COND(p_idx >= count);979980Char16String label = p_text.utf16();981MENUITEMINFOW item;982ZeroMemory(&item, sizeof(item));983item.cbSize = sizeof(item);984item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA;985if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {986item.dwTypeData = (LPWSTR)label.get_data();987SetMenuItemInfoW(md->menu, p_idx, true, &item);988}989}990991void NativeMenuWindows::set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid) {992ERR_FAIL_COND(p_idx < 0);993const MenuData *md = menus.get_or_null(p_rid);994ERR_FAIL_NULL(md);995int count = GetMenuItemCount(md->menu);996ERR_FAIL_COND(p_idx >= count);997998MenuData *md_sub = menus.get_or_null(p_submenu_rid);999ERR_FAIL_COND_MSG(md->menu == md_sub->menu, "Can't set submenu to self!");10001001MENUITEMINFOW item;1002ZeroMemory(&item, sizeof(item));1003item.cbSize = sizeof(item);1004item.fMask = MIIM_SUBMENU;1005if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {1006if (p_submenu_rid.is_valid()) {1007item.hSubMenu = md_sub->menu;1008} else {1009item.hSubMenu = nullptr;1010}1011SetMenuItemInfoW(md->menu, p_idx, true, &item);1012}1013}10141015void NativeMenuWindows::set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode) {1016// Not supported.1017}10181019void NativeMenuWindows::set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled) {1020ERR_FAIL_COND(p_idx < 0);1021const MenuData *md = menus.get_or_null(p_rid);1022ERR_FAIL_NULL(md);1023int count = GetMenuItemCount(md->menu);1024ERR_FAIL_COND(p_idx >= count);10251026MENUITEMINFOW item;1027ZeroMemory(&item, sizeof(item));1028item.cbSize = sizeof(item);1029item.fMask = MIIM_STATE;1030if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {1031if (p_disabled) {1032item.fState |= MFS_DISABLED;1033} else {1034item.fState &= ~MFS_DISABLED;1035}1036SetMenuItemInfoW(md->menu, p_idx, true, &item);1037}1038}10391040void NativeMenuWindows::set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden) {1041// Not supported.1042}10431044void NativeMenuWindows::set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip) {1045// Not supported.1046}10471048void NativeMenuWindows::set_item_state(const RID &p_rid, int p_idx, int p_state) {1049ERR_FAIL_COND(p_idx < 0);1050const MenuData *md = menus.get_or_null(p_rid);1051ERR_FAIL_NULL(md);1052int count = GetMenuItemCount(md->menu);1053ERR_FAIL_COND(p_idx >= count);10541055MENUITEMINFOW item;1056ZeroMemory(&item, sizeof(item));1057item.cbSize = sizeof(item);1058item.fMask = MIIM_DATA;1059if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {1060MenuItemData *item_data = (MenuItemData *)item.dwItemData;1061if (item_data) {1062item_data->state = p_state;1063}1064}1065}10661067void NativeMenuWindows::set_item_max_states(const RID &p_rid, int p_idx, int p_max_states) {1068ERR_FAIL_COND(p_idx < 0);1069const MenuData *md = menus.get_or_null(p_rid);1070ERR_FAIL_NULL(md);1071int count = GetMenuItemCount(md->menu);1072ERR_FAIL_COND(p_idx >= count);10731074MENUITEMINFOW item;1075ZeroMemory(&item, sizeof(item));1076item.cbSize = sizeof(item);1077item.fMask = MIIM_DATA;1078if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {1079MenuItemData *item_data = (MenuItemData *)item.dwItemData;1080if (item_data) {1081item_data->max_states = p_max_states;1082}1083}1084}10851086void NativeMenuWindows::set_item_icon(const RID &p_rid, int p_idx, const Ref<Texture2D> &p_icon) {1087ERR_FAIL_COND(p_idx < 0);1088const MenuData *md = menus.get_or_null(p_rid);1089ERR_FAIL_NULL(md);1090int count = GetMenuItemCount(md->menu);1091ERR_FAIL_COND(p_idx >= count);10921093MENUITEMINFOW item;1094ZeroMemory(&item, sizeof(item));1095item.cbSize = sizeof(item);1096item.fMask = MIIM_DATA | MIIM_BITMAP;1097if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {1098MenuItemData *item_data = (MenuItemData *)item.dwItemData;1099if (item_data) {1100if (item_data->bmp) {1101DeleteObject(item_data->bmp);1102}1103if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {1104item_data->img = p_icon->get_image();1105item_data->img = item_data->img->duplicate();1106if (item_data->img->is_compressed()) {1107item_data->img->decompress();1108}1109item_data->bmp = _make_bitmap(item_data->img);1110} else {1111item_data->img = Ref<Image>();1112item_data->bmp = nullptr;1113}1114item.hbmpItem = item_data->bmp;1115SetMenuItemInfoW(md->menu, p_idx, true, &item);1116}1117}1118}11191120void NativeMenuWindows::set_item_indentation_level(const RID &p_rid, int p_idx, int p_level) {1121// Not supported.1122}11231124int NativeMenuWindows::get_item_count(const RID &p_rid) const {1125const MenuData *md = menus.get_or_null(p_rid);1126ERR_FAIL_NULL_V(md, 0);11271128return GetMenuItemCount(md->menu);1129}11301131bool NativeMenuWindows::is_system_menu(const RID &p_rid) const {1132return false;1133}11341135void NativeMenuWindows::remove_item(const RID &p_rid, int p_idx) {1136ERR_FAIL_COND(p_idx < 0);1137const MenuData *md = menus.get_or_null(p_rid);1138ERR_FAIL_NULL(md);1139int count = GetMenuItemCount(md->menu);1140ERR_FAIL_COND(p_idx >= count);11411142MENUITEMINFOW item;1143ZeroMemory(&item, sizeof(item));1144item.cbSize = sizeof(item);1145item.fMask = MIIM_DATA;1146if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {1147MenuItemData *item_data = (MenuItemData *)item.dwItemData;1148if (item_data) {1149if (item_data->bmp) {1150DeleteObject(item_data->bmp);1151}1152memdelete(item_data);1153}1154}1155RemoveMenu(md->menu, p_idx, MF_BYPOSITION);1156}11571158void NativeMenuWindows::clear(const RID &p_rid) {1159const MenuData *md = menus.get_or_null(p_rid);1160ERR_FAIL_NULL(md);11611162MENUITEMINFOW item;1163int count = GetMenuItemCount(md->menu);1164for (int i = 0; i < count; i++) {1165ZeroMemory(&item, sizeof(item));1166item.cbSize = sizeof(item);1167item.fMask = MIIM_DATA;1168if (GetMenuItemInfoW(md->menu, 0, true, &item)) {1169MenuItemData *item_data = (MenuItemData *)item.dwItemData;1170if (item_data) {1171if (item_data->bmp) {1172DeleteObject(item_data->bmp);1173}1174memdelete(item_data);1175}1176}1177RemoveMenu(md->menu, 0, MF_BYPOSITION);1178}1179}11801181NativeMenuWindows::NativeMenuWindows() {}11821183NativeMenuWindows::~NativeMenuWindows() {}118411851186