native_menu_windows.cpp 36 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185
  1. /**************************************************************************/
  2. /* native_menu_windows.cpp */
  3. /**************************************************************************/
  4. /* This file is part of: */
  5. /* GODOT ENGINE */
  6. /* https://godotengine.org */
  7. /**************************************************************************/
  8. /* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
  9. /* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
  10. /* */
  11. /* Permission is hereby granted, free of charge, to any person obtaining */
  12. /* a copy of this software and associated documentation files (the */
  13. /* "Software"), to deal in the Software without restriction, including */
  14. /* without limitation the rights to use, copy, modify, merge, publish, */
  15. /* distribute, sublicense, and/or sell copies of the Software, and to */
  16. /* permit persons to whom the Software is furnished to do so, subject to */
  17. /* the following conditions: */
  18. /* */
  19. /* The above copyright notice and this permission notice shall be */
  20. /* included in all copies or substantial portions of the Software. */
  21. /* */
  22. /* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
  23. /* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
  24. /* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
  25. /* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
  26. /* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
  27. /* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
  28. /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
  29. /**************************************************************************/
  30. #include "native_menu_windows.h"
  31. #include "display_server_windows.h"
  32. #include "scene/resources/image_texture.h"
  33. HBITMAP NativeMenuWindows::_make_bitmap(const Ref<Image> &p_img) const {
  34. p_img->convert(Image::FORMAT_RGBA8);
  35. Vector2i texture_size = p_img->get_size();
  36. UINT image_size = texture_size.width * texture_size.height;
  37. COLORREF *buffer = nullptr;
  38. BITMAPV5HEADER bi;
  39. ZeroMemory(&bi, sizeof(bi));
  40. bi.bV5Size = sizeof(bi);
  41. bi.bV5Width = texture_size.width;
  42. bi.bV5Height = -texture_size.height;
  43. bi.bV5Planes = 1;
  44. bi.bV5BitCount = 32;
  45. bi.bV5Compression = BI_BITFIELDS;
  46. bi.bV5RedMask = 0x00ff0000;
  47. bi.bV5GreenMask = 0x0000ff00;
  48. bi.bV5BlueMask = 0x000000ff;
  49. bi.bV5AlphaMask = 0xff000000;
  50. HDC dc = GetDC(nullptr);
  51. HBITMAP bitmap = CreateDIBSection(dc, reinterpret_cast<BITMAPINFO *>(&bi), DIB_RGB_COLORS, reinterpret_cast<void **>(&buffer), nullptr, 0);
  52. for (UINT index = 0; index < image_size; index++) {
  53. int row_index = floor(index / texture_size.width);
  54. int column_index = (index % int(texture_size.width));
  55. const Color &c = p_img->get_pixel(column_index, row_index);
  56. *(buffer + index) = c.to_argb32();
  57. }
  58. ReleaseDC(nullptr, dc);
  59. return bitmap;
  60. }
  61. void NativeMenuWindows::_menu_activate(HMENU p_menu, int p_index) const {
  62. if (menu_lookup.has(p_menu)) {
  63. MenuData *md = menus.get_or_null(menu_lookup[p_menu]);
  64. if (md) {
  65. int count = GetMenuItemCount(md->menu);
  66. if (p_index >= 0 && p_index < count) {
  67. MENUITEMINFOW item;
  68. ZeroMemory(&item, sizeof(item));
  69. item.cbSize = sizeof(item);
  70. item.fMask = MIIM_STATE | MIIM_DATA;
  71. if (GetMenuItemInfoW(md->menu, p_index, true, &item)) {
  72. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  73. if (item_data) {
  74. if (item_data->callback.is_valid()) {
  75. Variant ret;
  76. Callable::CallError ce;
  77. const Variant *args[1] = { &item_data->meta };
  78. item_data->callback.callp(args, 1, ret, ce);
  79. if (ce.error != Callable::CallError::CALL_OK) {
  80. ERR_PRINT(vformat("Failed to execute menu callback: %s.", Variant::get_callable_error_text(item_data->callback, args, 1, ce)));
  81. }
  82. }
  83. }
  84. }
  85. }
  86. }
  87. }
  88. }
  89. bool NativeMenuWindows::has_feature(Feature p_feature) const {
  90. switch (p_feature) {
  91. // case FEATURE_GLOBAL_MENU:
  92. // case FEATURE_OPEN_CLOSE_CALLBACK:
  93. // case FEATURE_HOVER_CALLBACK:
  94. // case FEATURE_KEY_CALLBACK:
  95. case FEATURE_POPUP_MENU:
  96. return true;
  97. default:
  98. return false;
  99. }
  100. }
  101. bool NativeMenuWindows::has_system_menu(SystemMenus p_menu_id) const {
  102. return false;
  103. }
  104. RID NativeMenuWindows::get_system_menu(SystemMenus p_menu_id) const {
  105. return RID();
  106. }
  107. RID NativeMenuWindows::create_menu() {
  108. MenuData *md = memnew(MenuData);
  109. md->menu = CreatePopupMenu();
  110. MENUINFO menu_info;
  111. ZeroMemory(&menu_info, sizeof(menu_info));
  112. menu_info.cbSize = sizeof(menu_info);
  113. menu_info.fMask = MIM_STYLE;
  114. menu_info.dwStyle = MNS_NOTIFYBYPOS;
  115. SetMenuInfo(md->menu, &menu_info);
  116. RID rid = menus.make_rid(md);
  117. menu_lookup[md->menu] = rid;
  118. return rid;
  119. }
  120. bool NativeMenuWindows::has_menu(const RID &p_rid) const {
  121. return menus.owns(p_rid);
  122. }
  123. void NativeMenuWindows::free_menu(const RID &p_rid) {
  124. MenuData *md = menus.get_or_null(p_rid);
  125. if (md) {
  126. clear(p_rid);
  127. DestroyMenu(md->menu);
  128. menus.free(p_rid);
  129. menu_lookup.erase(md->menu);
  130. memdelete(md);
  131. }
  132. }
  133. Size2 NativeMenuWindows::get_size(const RID &p_rid) const {
  134. const MenuData *md = menus.get_or_null(p_rid);
  135. ERR_FAIL_NULL_V(md, Size2());
  136. Size2 size;
  137. int count = GetMenuItemCount(md->menu);
  138. for (int i = 0; i < count; i++) {
  139. RECT rect;
  140. if (GetMenuItemRect(nullptr, md->menu, i, &rect)) {
  141. size.x = MAX(size.x, rect.right - rect.left);
  142. size.y += rect.bottom - rect.top;
  143. }
  144. }
  145. return size;
  146. }
  147. void NativeMenuWindows::popup(const RID &p_rid, const Vector2i &p_position) {
  148. const MenuData *md = menus.get_or_null(p_rid);
  149. ERR_FAIL_NULL(md);
  150. HWND hwnd = (HWND)DisplayServer::get_singleton()->window_get_native_handle(DisplayServer::WINDOW_HANDLE, DisplayServer::MAIN_WINDOW_ID);
  151. UINT flags = TPM_HORIZONTAL | TPM_LEFTALIGN | TPM_TOPALIGN | TPM_LEFTBUTTON | TPM_VERPOSANIMATION;
  152. if (md->is_rtl) {
  153. flags |= TPM_LAYOUTRTL;
  154. }
  155. SetForegroundWindow(hwnd);
  156. TrackPopupMenuEx(md->menu, flags, p_position.x, p_position.y, hwnd, nullptr);
  157. if (md->close_cb.is_valid()) {
  158. Variant ret;
  159. Callable::CallError ce;
  160. md->close_cb.callp(nullptr, 0, ret, ce);
  161. if (ce.error != Callable::CallError::CALL_OK) {
  162. ERR_PRINT(vformat("Failed to execute popup close callback: %s.", Variant::get_callable_error_text(md->close_cb, nullptr, 0, ce)));
  163. }
  164. }
  165. PostMessage(hwnd, WM_NULL, 0, 0);
  166. }
  167. void NativeMenuWindows::set_interface_direction(const RID &p_rid, bool p_is_rtl) {
  168. MenuData *md = menus.get_or_null(p_rid);
  169. ERR_FAIL_NULL(md);
  170. if (md->is_rtl == p_is_rtl) {
  171. return;
  172. }
  173. md->is_rtl = p_is_rtl;
  174. }
  175. void NativeMenuWindows::set_popup_open_callback(const RID &p_rid, const Callable &p_callback) {
  176. // Not supported.
  177. }
  178. Callable NativeMenuWindows::get_popup_open_callback(const RID &p_rid) const {
  179. // Not supported.
  180. return Callable();
  181. }
  182. void NativeMenuWindows::set_popup_close_callback(const RID &p_rid, const Callable &p_callback) {
  183. MenuData *md = menus.get_or_null(p_rid);
  184. ERR_FAIL_NULL(md);
  185. md->close_cb = p_callback;
  186. }
  187. Callable NativeMenuWindows::get_popup_close_callback(const RID &p_rid) const {
  188. const MenuData *md = menus.get_or_null(p_rid);
  189. ERR_FAIL_NULL_V(md, Callable());
  190. return md->close_cb;
  191. }
  192. void NativeMenuWindows::set_minimum_width(const RID &p_rid, float p_width) {
  193. // Not supported.
  194. }
  195. float NativeMenuWindows::get_minimum_width(const RID &p_rid) const {
  196. // Not supported.
  197. return 0.f;
  198. }
  199. bool NativeMenuWindows::is_opened(const RID &p_rid) const {
  200. // Not supported.
  201. return false;
  202. }
  203. int NativeMenuWindows::add_submenu_item(const RID &p_rid, const String &p_label, const RID &p_submenu_rid, const Variant &p_tag, int p_index) {
  204. MenuData *md = menus.get_or_null(p_rid);
  205. MenuData *md_sub = menus.get_or_null(p_submenu_rid);
  206. ERR_FAIL_NULL_V(md, -1);
  207. ERR_FAIL_NULL_V(md_sub, -1);
  208. ERR_FAIL_COND_V_MSG(md->menu == md_sub->menu, -1, "Can't set submenu to self!");
  209. if (p_index == -1) {
  210. p_index = GetMenuItemCount(md->menu);
  211. } else {
  212. p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));
  213. }
  214. MenuItemData *item_data = memnew(MenuItemData);
  215. item_data->meta = p_tag;
  216. item_data->checkable_type = CHECKABLE_TYPE_NONE;
  217. item_data->max_states = 0;
  218. item_data->state = 0;
  219. Char16String label = p_label.utf16();
  220. MENUITEMINFOW item;
  221. ZeroMemory(&item, sizeof(item));
  222. item.cbSize = sizeof(item);
  223. item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA | MIIM_SUBMENU;
  224. item.fType = MFT_STRING;
  225. item.hSubMenu = md_sub->menu;
  226. item.dwItemData = (ULONG_PTR)item_data;
  227. item.dwTypeData = (LPWSTR)label.get_data();
  228. if (!InsertMenuItemW(md->menu, p_index, true, &item)) {
  229. memdelete(item_data);
  230. return -1;
  231. }
  232. return p_index;
  233. }
  234. int 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) {
  235. const MenuData *md = menus.get_or_null(p_rid);
  236. ERR_FAIL_NULL_V(md, -1);
  237. if (p_index == -1) {
  238. p_index = GetMenuItemCount(md->menu);
  239. } else {
  240. p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));
  241. }
  242. MenuItemData *item_data = memnew(MenuItemData);
  243. item_data->callback = p_callback;
  244. item_data->meta = p_tag;
  245. item_data->checkable_type = CHECKABLE_TYPE_NONE;
  246. item_data->max_states = 0;
  247. item_data->state = 0;
  248. Char16String label = p_label.utf16();
  249. MENUITEMINFOW item;
  250. ZeroMemory(&item, sizeof(item));
  251. item.cbSize = sizeof(item);
  252. item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA;
  253. item.fType = MFT_STRING;
  254. item.dwItemData = (ULONG_PTR)item_data;
  255. item.dwTypeData = (LPWSTR)label.get_data();
  256. if (!InsertMenuItemW(md->menu, p_index, true, &item)) {
  257. memdelete(item_data);
  258. return -1;
  259. }
  260. return p_index;
  261. }
  262. int 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) {
  263. const MenuData *md = menus.get_or_null(p_rid);
  264. ERR_FAIL_NULL_V(md, -1);
  265. if (p_index == -1) {
  266. p_index = GetMenuItemCount(md->menu);
  267. } else {
  268. p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));
  269. }
  270. MenuItemData *item_data = memnew(MenuItemData);
  271. item_data->callback = p_callback;
  272. item_data->meta = p_tag;
  273. item_data->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
  274. item_data->max_states = 0;
  275. item_data->state = 0;
  276. Char16String label = p_label.utf16();
  277. MENUITEMINFOW item;
  278. ZeroMemory(&item, sizeof(item));
  279. item.cbSize = sizeof(item);
  280. item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA;
  281. item.fType = MFT_STRING;
  282. item.dwItemData = (ULONG_PTR)item_data;
  283. item.dwTypeData = (LPWSTR)label.get_data();
  284. if (!InsertMenuItemW(md->menu, p_index, true, &item)) {
  285. memdelete(item_data);
  286. return -1;
  287. }
  288. return p_index;
  289. }
  290. int 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) {
  291. const MenuData *md = menus.get_or_null(p_rid);
  292. ERR_FAIL_NULL_V(md, -1);
  293. if (p_index == -1) {
  294. p_index = GetMenuItemCount(md->menu);
  295. } else {
  296. p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));
  297. }
  298. MenuItemData *item_data = memnew(MenuItemData);
  299. item_data->callback = p_callback;
  300. item_data->meta = p_tag;
  301. item_data->checkable_type = CHECKABLE_TYPE_NONE;
  302. item_data->max_states = 0;
  303. item_data->state = 0;
  304. if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
  305. item_data->img = p_icon->get_image();
  306. item_data->img = item_data->img->duplicate();
  307. if (item_data->img->is_compressed()) {
  308. item_data->img->decompress();
  309. }
  310. item_data->bmp = _make_bitmap(item_data->img);
  311. }
  312. Char16String label = p_label.utf16();
  313. MENUITEMINFOW item;
  314. ZeroMemory(&item, sizeof(item));
  315. item.cbSize = sizeof(item);
  316. item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA | MIIM_BITMAP;
  317. item.fType = MFT_STRING;
  318. item.dwItemData = (ULONG_PTR)item_data;
  319. item.dwTypeData = (LPWSTR)label.get_data();
  320. item.hbmpItem = item_data->bmp;
  321. if (!InsertMenuItemW(md->menu, p_index, true, &item)) {
  322. memdelete(item_data);
  323. return -1;
  324. }
  325. return p_index;
  326. }
  327. int 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) {
  328. const MenuData *md = menus.get_or_null(p_rid);
  329. ERR_FAIL_NULL_V(md, -1);
  330. if (p_index == -1) {
  331. p_index = GetMenuItemCount(md->menu);
  332. } else {
  333. p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));
  334. }
  335. MenuItemData *item_data = memnew(MenuItemData);
  336. item_data->callback = p_callback;
  337. item_data->meta = p_tag;
  338. item_data->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
  339. item_data->max_states = 0;
  340. item_data->state = 0;
  341. if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
  342. item_data->img = p_icon->get_image();
  343. item_data->img = item_data->img->duplicate();
  344. if (item_data->img->is_compressed()) {
  345. item_data->img->decompress();
  346. }
  347. item_data->bmp = _make_bitmap(item_data->img);
  348. }
  349. Char16String label = p_label.utf16();
  350. MENUITEMINFOW item;
  351. ZeroMemory(&item, sizeof(item));
  352. item.cbSize = sizeof(item);
  353. item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA | MIIM_BITMAP;
  354. item.fType = MFT_STRING;
  355. item.dwItemData = (ULONG_PTR)item_data;
  356. item.dwTypeData = (LPWSTR)label.get_data();
  357. item.hbmpItem = item_data->bmp;
  358. if (!InsertMenuItemW(md->menu, p_index, true, &item)) {
  359. memdelete(item_data);
  360. return -1;
  361. }
  362. return p_index;
  363. }
  364. int 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) {
  365. const MenuData *md = menus.get_or_null(p_rid);
  366. ERR_FAIL_NULL_V(md, -1);
  367. if (p_index == -1) {
  368. p_index = GetMenuItemCount(md->menu);
  369. } else {
  370. p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));
  371. }
  372. MenuItemData *item_data = memnew(MenuItemData);
  373. item_data->callback = p_callback;
  374. item_data->meta = p_tag;
  375. item_data->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;
  376. item_data->max_states = 0;
  377. item_data->state = 0;
  378. Char16String label = p_label.utf16();
  379. MENUITEMINFOW item;
  380. ZeroMemory(&item, sizeof(item));
  381. item.cbSize = sizeof(item);
  382. item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA;
  383. item.fType = MFT_STRING | MFT_RADIOCHECK;
  384. item.dwItemData = (ULONG_PTR)item_data;
  385. item.dwTypeData = (LPWSTR)label.get_data();
  386. if (!InsertMenuItemW(md->menu, p_index, true, &item)) {
  387. memdelete(item_data);
  388. return -1;
  389. }
  390. return p_index;
  391. }
  392. int 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) {
  393. const MenuData *md = menus.get_or_null(p_rid);
  394. ERR_FAIL_NULL_V(md, -1);
  395. if (p_index == -1) {
  396. p_index = GetMenuItemCount(md->menu);
  397. } else {
  398. p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));
  399. }
  400. MenuItemData *item_data = memnew(MenuItemData);
  401. item_data->callback = p_callback;
  402. item_data->meta = p_tag;
  403. item_data->checkable_type = CHECKABLE_TYPE_RADIO_BUTTON;
  404. item_data->max_states = 0;
  405. item_data->state = 0;
  406. if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
  407. item_data->img = p_icon->get_image();
  408. item_data->img = item_data->img->duplicate();
  409. if (item_data->img->is_compressed()) {
  410. item_data->img->decompress();
  411. }
  412. item_data->bmp = _make_bitmap(item_data->img);
  413. }
  414. Char16String label = p_label.utf16();
  415. MENUITEMINFOW item;
  416. ZeroMemory(&item, sizeof(item));
  417. item.cbSize = sizeof(item);
  418. item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA | MIIM_BITMAP;
  419. item.fType = MFT_STRING | MFT_RADIOCHECK;
  420. item.dwItemData = (ULONG_PTR)item_data;
  421. item.dwTypeData = (LPWSTR)label.get_data();
  422. item.hbmpItem = item_data->bmp;
  423. if (!InsertMenuItemW(md->menu, p_index, true, &item)) {
  424. memdelete(item_data);
  425. return -1;
  426. }
  427. return p_index;
  428. }
  429. int 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) {
  430. const MenuData *md = menus.get_or_null(p_rid);
  431. ERR_FAIL_NULL_V(md, -1);
  432. if (p_index == -1) {
  433. p_index = GetMenuItemCount(md->menu);
  434. } else {
  435. p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));
  436. }
  437. MenuItemData *item_data = memnew(MenuItemData);
  438. item_data->callback = p_callback;
  439. item_data->meta = p_tag;
  440. item_data->checkable_type = CHECKABLE_TYPE_NONE;
  441. item_data->max_states = p_max_states;
  442. item_data->state = p_default_state;
  443. Char16String label = p_label.utf16();
  444. MENUITEMINFOW item;
  445. ZeroMemory(&item, sizeof(item));
  446. item.cbSize = sizeof(item);
  447. item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA;
  448. item.fType = MFT_STRING;
  449. item.dwItemData = (ULONG_PTR)item_data;
  450. item.dwTypeData = (LPWSTR)label.get_data();
  451. if (!InsertMenuItemW(md->menu, p_index, true, &item)) {
  452. memdelete(item_data);
  453. return -1;
  454. }
  455. return p_index;
  456. }
  457. int NativeMenuWindows::add_separator(const RID &p_rid, int p_index) {
  458. const MenuData *md = menus.get_or_null(p_rid);
  459. ERR_FAIL_NULL_V(md, -1);
  460. if (p_index == -1) {
  461. p_index = GetMenuItemCount(md->menu);
  462. } else {
  463. p_index = CLAMP(p_index, 0, GetMenuItemCount(md->menu));
  464. }
  465. MenuItemData *item_data = memnew(MenuItemData);
  466. item_data->checkable_type = CHECKABLE_TYPE_NONE;
  467. item_data->max_states = 0;
  468. item_data->state = 0;
  469. MENUITEMINFOW item;
  470. ZeroMemory(&item, sizeof(item));
  471. item.cbSize = sizeof(item);
  472. item.fMask = MIIM_FTYPE | MIIM_DATA;
  473. item.fType = MFT_SEPARATOR;
  474. item.dwItemData = (ULONG_PTR)item_data;
  475. if (!InsertMenuItemW(md->menu, p_index, true, &item)) {
  476. memdelete(item_data);
  477. return -1;
  478. }
  479. return p_index;
  480. }
  481. int NativeMenuWindows::find_item_index_with_text(const RID &p_rid, const String &p_text) const {
  482. const MenuData *md = menus.get_or_null(p_rid);
  483. ERR_FAIL_NULL_V(md, -1);
  484. MENUITEMINFOW item;
  485. int count = GetMenuItemCount(md->menu);
  486. for (int i = 0; i < count; i++) {
  487. ZeroMemory(&item, sizeof(item));
  488. item.cbSize = sizeof(item);
  489. item.fMask = MIIM_STRING;
  490. item.dwTypeData = nullptr;
  491. if (GetMenuItemInfoW(md->menu, i, true, &item)) {
  492. item.cch++;
  493. Char16String str;
  494. str.resize(item.cch);
  495. item.dwTypeData = (LPWSTR)str.ptrw();
  496. if (GetMenuItemInfoW(md->menu, i, true, &item)) {
  497. if (String::utf16((const char16_t *)str.get_data()) == p_text) {
  498. return i;
  499. }
  500. }
  501. }
  502. }
  503. return -1;
  504. }
  505. int NativeMenuWindows::find_item_index_with_tag(const RID &p_rid, const Variant &p_tag) const {
  506. const MenuData *md = menus.get_or_null(p_rid);
  507. ERR_FAIL_NULL_V(md, -1);
  508. MENUITEMINFOW item;
  509. int count = GetMenuItemCount(md->menu);
  510. for (int i = 0; i < count; i++) {
  511. ZeroMemory(&item, sizeof(item));
  512. item.cbSize = sizeof(item);
  513. item.fMask = MIIM_DATA;
  514. if (GetMenuItemInfoW(md->menu, i, true, &item)) {
  515. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  516. if (item_data) {
  517. if (item_data->meta == p_tag) {
  518. return i;
  519. }
  520. }
  521. }
  522. }
  523. return -1;
  524. }
  525. bool NativeMenuWindows::is_item_checked(const RID &p_rid, int p_idx) const {
  526. ERR_FAIL_COND_V(p_idx < 0, false);
  527. const MenuData *md = menus.get_or_null(p_rid);
  528. ERR_FAIL_NULL_V(md, false);
  529. int count = GetMenuItemCount(md->menu);
  530. ERR_FAIL_COND_V(p_idx >= count, false);
  531. MENUITEMINFOW item;
  532. ZeroMemory(&item, sizeof(item));
  533. item.cbSize = sizeof(item);
  534. item.fMask = MIIM_STATE | MIIM_DATA;
  535. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  536. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  537. if (item_data) {
  538. return item_data->checked;
  539. }
  540. }
  541. return false;
  542. }
  543. bool NativeMenuWindows::is_item_checkable(const RID &p_rid, int p_idx) const {
  544. ERR_FAIL_COND_V(p_idx < 0, false);
  545. const MenuData *md = menus.get_or_null(p_rid);
  546. ERR_FAIL_NULL_V(md, false);
  547. int count = GetMenuItemCount(md->menu);
  548. ERR_FAIL_COND_V(p_idx >= count, false);
  549. MENUITEMINFOW item;
  550. ZeroMemory(&item, sizeof(item));
  551. item.cbSize = sizeof(item);
  552. item.fMask = MIIM_DATA;
  553. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  554. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  555. if (item_data) {
  556. return item_data->checkable_type == CHECKABLE_TYPE_CHECK_BOX;
  557. }
  558. }
  559. return false;
  560. }
  561. bool NativeMenuWindows::is_item_radio_checkable(const RID &p_rid, int p_idx) const {
  562. ERR_FAIL_COND_V(p_idx < 0, false);
  563. const MenuData *md = menus.get_or_null(p_rid);
  564. ERR_FAIL_NULL_V(md, false);
  565. int count = GetMenuItemCount(md->menu);
  566. ERR_FAIL_COND_V(p_idx >= count, false);
  567. MENUITEMINFOW item;
  568. ZeroMemory(&item, sizeof(item));
  569. item.cbSize = sizeof(item);
  570. item.fMask = MIIM_DATA;
  571. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  572. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  573. if (item_data) {
  574. return item_data->checkable_type == CHECKABLE_TYPE_RADIO_BUTTON;
  575. }
  576. }
  577. return false;
  578. }
  579. Callable NativeMenuWindows::get_item_callback(const RID &p_rid, int p_idx) const {
  580. ERR_FAIL_COND_V(p_idx < 0, Callable());
  581. const MenuData *md = menus.get_or_null(p_rid);
  582. ERR_FAIL_NULL_V(md, Callable());
  583. int count = GetMenuItemCount(md->menu);
  584. ERR_FAIL_COND_V(p_idx >= count, Callable());
  585. MENUITEMINFOW item;
  586. ZeroMemory(&item, sizeof(item));
  587. item.cbSize = sizeof(item);
  588. item.fMask = MIIM_DATA;
  589. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  590. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  591. if (item_data) {
  592. return item_data->callback;
  593. }
  594. }
  595. return Callable();
  596. }
  597. Callable NativeMenuWindows::get_item_key_callback(const RID &p_rid, int p_idx) const {
  598. // Not supported.
  599. return Callable();
  600. }
  601. Variant NativeMenuWindows::get_item_tag(const RID &p_rid, int p_idx) const {
  602. ERR_FAIL_COND_V(p_idx < 0, Variant());
  603. const MenuData *md = menus.get_or_null(p_rid);
  604. ERR_FAIL_NULL_V(md, Variant());
  605. int count = GetMenuItemCount(md->menu);
  606. ERR_FAIL_COND_V(p_idx >= count, Variant());
  607. MENUITEMINFOW item;
  608. ZeroMemory(&item, sizeof(item));
  609. item.cbSize = sizeof(item);
  610. item.fMask = MIIM_DATA;
  611. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  612. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  613. if (item_data) {
  614. return item_data->meta;
  615. }
  616. }
  617. return Variant();
  618. }
  619. String NativeMenuWindows::get_item_text(const RID &p_rid, int p_idx) const {
  620. ERR_FAIL_COND_V(p_idx < 0, String());
  621. const MenuData *md = menus.get_or_null(p_rid);
  622. ERR_FAIL_NULL_V(md, String());
  623. int count = GetMenuItemCount(md->menu);
  624. ERR_FAIL_COND_V(p_idx >= count, String());
  625. MENUITEMINFOW item;
  626. ZeroMemory(&item, sizeof(item));
  627. item.cbSize = sizeof(item);
  628. item.fMask = MIIM_STRING;
  629. item.dwTypeData = nullptr;
  630. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  631. item.cch++;
  632. Char16String str;
  633. str.resize(item.cch);
  634. item.dwTypeData = (LPWSTR)str.ptrw();
  635. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  636. return String::utf16((const char16_t *)str.get_data());
  637. }
  638. }
  639. return String();
  640. }
  641. RID NativeMenuWindows::get_item_submenu(const RID &p_rid, int p_idx) const {
  642. ERR_FAIL_COND_V(p_idx < 0, RID());
  643. const MenuData *md = menus.get_or_null(p_rid);
  644. ERR_FAIL_NULL_V(md, RID());
  645. int count = GetMenuItemCount(md->menu);
  646. ERR_FAIL_COND_V(p_idx >= count, RID());
  647. MENUITEMINFOW item;
  648. ZeroMemory(&item, sizeof(item));
  649. item.cbSize = sizeof(item);
  650. item.fMask = MIIM_SUBMENU;
  651. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  652. if (menu_lookup.has(item.hSubMenu)) {
  653. return menu_lookup[item.hSubMenu];
  654. }
  655. }
  656. return RID();
  657. }
  658. Key NativeMenuWindows::get_item_accelerator(const RID &p_rid, int p_idx) const {
  659. // Not supported.
  660. return Key::NONE;
  661. }
  662. bool NativeMenuWindows::is_item_disabled(const RID &p_rid, int p_idx) const {
  663. ERR_FAIL_COND_V(p_idx < 0, false);
  664. const MenuData *md = menus.get_or_null(p_rid);
  665. ERR_FAIL_NULL_V(md, false);
  666. int count = GetMenuItemCount(md->menu);
  667. ERR_FAIL_COND_V(p_idx >= count, false);
  668. MENUITEMINFOW item;
  669. ZeroMemory(&item, sizeof(item));
  670. item.cbSize = sizeof(item);
  671. item.fMask = MIIM_STATE;
  672. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  673. return (item.fState & MFS_DISABLED) == MFS_DISABLED;
  674. }
  675. return false;
  676. }
  677. bool NativeMenuWindows::is_item_hidden(const RID &p_rid, int p_idx) const {
  678. // Not supported.
  679. return false;
  680. }
  681. String NativeMenuWindows::get_item_tooltip(const RID &p_rid, int p_idx) const {
  682. // Not supported.
  683. return String();
  684. }
  685. int NativeMenuWindows::get_item_state(const RID &p_rid, int p_idx) const {
  686. ERR_FAIL_COND_V(p_idx < 0, -1);
  687. const MenuData *md = menus.get_or_null(p_rid);
  688. ERR_FAIL_NULL_V(md, -1);
  689. int count = GetMenuItemCount(md->menu);
  690. ERR_FAIL_COND_V(p_idx >= count, -1);
  691. MENUITEMINFOW item;
  692. ZeroMemory(&item, sizeof(item));
  693. item.cbSize = sizeof(item);
  694. item.fMask = MIIM_DATA;
  695. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  696. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  697. if (item_data) {
  698. return item_data->state;
  699. }
  700. }
  701. return -1;
  702. }
  703. int NativeMenuWindows::get_item_max_states(const RID &p_rid, int p_idx) const {
  704. ERR_FAIL_COND_V(p_idx < 0, -1);
  705. const MenuData *md = menus.get_or_null(p_rid);
  706. ERR_FAIL_NULL_V(md, -1);
  707. int count = GetMenuItemCount(md->menu);
  708. ERR_FAIL_COND_V(p_idx >= count, -1);
  709. MENUITEMINFOW item;
  710. ZeroMemory(&item, sizeof(item));
  711. item.cbSize = sizeof(item);
  712. item.fMask = MIIM_DATA;
  713. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  714. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  715. if (item_data) {
  716. return item_data->max_states;
  717. }
  718. }
  719. return -1;
  720. }
  721. Ref<Texture2D> NativeMenuWindows::get_item_icon(const RID &p_rid, int p_idx) const {
  722. ERR_FAIL_COND_V(p_idx < 0, Ref<Texture2D>());
  723. const MenuData *md = menus.get_or_null(p_rid);
  724. ERR_FAIL_NULL_V(md, Ref<Texture2D>());
  725. int count = GetMenuItemCount(md->menu);
  726. ERR_FAIL_COND_V(p_idx >= count, Ref<Texture2D>());
  727. MENUITEMINFOW item;
  728. ZeroMemory(&item, sizeof(item));
  729. item.cbSize = sizeof(item);
  730. item.fMask = MIIM_DATA;
  731. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  732. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  733. if (item_data) {
  734. return ImageTexture::create_from_image(item_data->img);
  735. }
  736. }
  737. return Ref<Texture2D>();
  738. }
  739. int NativeMenuWindows::get_item_indentation_level(const RID &p_rid, int p_idx) const {
  740. // Not supported.
  741. return 0;
  742. }
  743. void NativeMenuWindows::set_item_checked(const RID &p_rid, int p_idx, bool p_checked) {
  744. ERR_FAIL_COND(p_idx < 0);
  745. const MenuData *md = menus.get_or_null(p_rid);
  746. ERR_FAIL_NULL(md);
  747. int count = GetMenuItemCount(md->menu);
  748. ERR_FAIL_COND(p_idx >= count);
  749. MENUITEMINFOW item;
  750. ZeroMemory(&item, sizeof(item));
  751. item.cbSize = sizeof(item);
  752. item.fMask = MIIM_STATE | MIIM_DATA;
  753. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  754. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  755. if (item_data) {
  756. item_data->checked = p_checked;
  757. if (p_checked) {
  758. item.fState |= MFS_CHECKED;
  759. } else {
  760. item.fState &= ~MFS_CHECKED;
  761. }
  762. }
  763. SetMenuItemInfoW(md->menu, p_idx, true, &item);
  764. }
  765. }
  766. void NativeMenuWindows::set_item_checkable(const RID &p_rid, int p_idx, bool p_checkable) {
  767. ERR_FAIL_COND(p_idx < 0);
  768. const MenuData *md = menus.get_or_null(p_rid);
  769. ERR_FAIL_NULL(md);
  770. int count = GetMenuItemCount(md->menu);
  771. ERR_FAIL_COND(p_idx >= count);
  772. MENUITEMINFOW item;
  773. ZeroMemory(&item, sizeof(item));
  774. item.cbSize = sizeof(item);
  775. item.fMask = MIIM_FTYPE | MIIM_DATA;
  776. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  777. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  778. if (item_data) {
  779. item.fType &= ~MFT_RADIOCHECK;
  780. item_data->checkable_type = (p_checkable) ? CHECKABLE_TYPE_CHECK_BOX : CHECKABLE_TYPE_NONE;
  781. SetMenuItemInfoW(md->menu, p_idx, true, &item);
  782. }
  783. }
  784. }
  785. void NativeMenuWindows::set_item_radio_checkable(const RID &p_rid, int p_idx, bool p_checkable) {
  786. ERR_FAIL_COND(p_idx < 0);
  787. const MenuData *md = menus.get_or_null(p_rid);
  788. ERR_FAIL_NULL(md);
  789. int count = GetMenuItemCount(md->menu);
  790. ERR_FAIL_COND(p_idx >= count);
  791. MENUITEMINFOW item;
  792. ZeroMemory(&item, sizeof(item));
  793. item.cbSize = sizeof(item);
  794. item.fMask = MIIM_FTYPE | MIIM_DATA;
  795. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  796. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  797. if (item_data) {
  798. if (p_checkable) {
  799. item.fType |= MFT_RADIOCHECK;
  800. item_data->checkable_type = CHECKABLE_TYPE_CHECK_BOX;
  801. } else {
  802. item.fType &= ~MFT_RADIOCHECK;
  803. item_data->checkable_type = CHECKABLE_TYPE_NONE;
  804. }
  805. SetMenuItemInfoW(md->menu, p_idx, true, &item);
  806. }
  807. }
  808. }
  809. void NativeMenuWindows::set_item_callback(const RID &p_rid, int p_idx, const Callable &p_callback) {
  810. ERR_FAIL_COND(p_idx < 0);
  811. const MenuData *md = menus.get_or_null(p_rid);
  812. ERR_FAIL_NULL(md);
  813. int count = GetMenuItemCount(md->menu);
  814. ERR_FAIL_COND(p_idx >= count);
  815. MENUITEMINFOW item;
  816. ZeroMemory(&item, sizeof(item));
  817. item.cbSize = sizeof(item);
  818. item.fMask = MIIM_DATA;
  819. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  820. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  821. if (item_data) {
  822. item_data->callback = p_callback;
  823. }
  824. }
  825. }
  826. void NativeMenuWindows::set_item_key_callback(const RID &p_rid, int p_idx, const Callable &p_key_callback) {
  827. // Not supported.
  828. }
  829. void NativeMenuWindows::set_item_hover_callbacks(const RID &p_rid, int p_idx, const Callable &p_callback) {
  830. // Not supported.
  831. }
  832. void NativeMenuWindows::set_item_tag(const RID &p_rid, int p_idx, const Variant &p_tag) {
  833. ERR_FAIL_COND(p_idx < 0);
  834. const MenuData *md = menus.get_or_null(p_rid);
  835. ERR_FAIL_NULL(md);
  836. int count = GetMenuItemCount(md->menu);
  837. ERR_FAIL_COND(p_idx >= count);
  838. MENUITEMINFOW item;
  839. ZeroMemory(&item, sizeof(item));
  840. item.cbSize = sizeof(item);
  841. item.fMask = MIIM_DATA;
  842. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  843. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  844. if (item_data) {
  845. item_data->meta = p_tag;
  846. }
  847. }
  848. }
  849. void NativeMenuWindows::set_item_text(const RID &p_rid, int p_idx, const String &p_text) {
  850. ERR_FAIL_COND(p_idx < 0);
  851. const MenuData *md = menus.get_or_null(p_rid);
  852. ERR_FAIL_NULL(md);
  853. int count = GetMenuItemCount(md->menu);
  854. ERR_FAIL_COND(p_idx >= count);
  855. Char16String label = p_text.utf16();
  856. MENUITEMINFOW item;
  857. ZeroMemory(&item, sizeof(item));
  858. item.cbSize = sizeof(item);
  859. item.fMask = MIIM_FTYPE | MIIM_STRING | MIIM_DATA;
  860. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  861. item.dwTypeData = (LPWSTR)label.get_data();
  862. SetMenuItemInfoW(md->menu, p_idx, true, &item);
  863. }
  864. }
  865. void NativeMenuWindows::set_item_submenu(const RID &p_rid, int p_idx, const RID &p_submenu_rid) {
  866. ERR_FAIL_COND(p_idx < 0);
  867. const MenuData *md = menus.get_or_null(p_rid);
  868. ERR_FAIL_NULL(md);
  869. int count = GetMenuItemCount(md->menu);
  870. ERR_FAIL_COND(p_idx >= count);
  871. MenuData *md_sub = menus.get_or_null(p_submenu_rid);
  872. ERR_FAIL_COND_MSG(md->menu == md_sub->menu, "Can't set submenu to self!");
  873. MENUITEMINFOW item;
  874. ZeroMemory(&item, sizeof(item));
  875. item.cbSize = sizeof(item);
  876. item.fMask = MIIM_SUBMENU;
  877. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  878. if (p_submenu_rid.is_valid()) {
  879. item.hSubMenu = md_sub->menu;
  880. } else {
  881. item.hSubMenu = nullptr;
  882. }
  883. SetMenuItemInfoW(md->menu, p_idx, true, &item);
  884. }
  885. }
  886. void NativeMenuWindows::set_item_accelerator(const RID &p_rid, int p_idx, Key p_keycode) {
  887. // Not supported.
  888. }
  889. void NativeMenuWindows::set_item_disabled(const RID &p_rid, int p_idx, bool p_disabled) {
  890. ERR_FAIL_COND(p_idx < 0);
  891. const MenuData *md = menus.get_or_null(p_rid);
  892. ERR_FAIL_NULL(md);
  893. int count = GetMenuItemCount(md->menu);
  894. ERR_FAIL_COND(p_idx >= count);
  895. MENUITEMINFOW item;
  896. ZeroMemory(&item, sizeof(item));
  897. item.cbSize = sizeof(item);
  898. item.fMask = MIIM_STATE;
  899. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  900. if (p_disabled) {
  901. item.fState |= MFS_DISABLED;
  902. } else {
  903. item.fState &= ~MFS_DISABLED;
  904. }
  905. SetMenuItemInfoW(md->menu, p_idx, true, &item);
  906. }
  907. }
  908. void NativeMenuWindows::set_item_hidden(const RID &p_rid, int p_idx, bool p_hidden) {
  909. // Not supported.
  910. }
  911. void NativeMenuWindows::set_item_tooltip(const RID &p_rid, int p_idx, const String &p_tooltip) {
  912. // Not supported.
  913. }
  914. void NativeMenuWindows::set_item_state(const RID &p_rid, int p_idx, int p_state) {
  915. ERR_FAIL_COND(p_idx < 0);
  916. const MenuData *md = menus.get_or_null(p_rid);
  917. ERR_FAIL_NULL(md);
  918. int count = GetMenuItemCount(md->menu);
  919. ERR_FAIL_COND(p_idx >= count);
  920. MENUITEMINFOW item;
  921. ZeroMemory(&item, sizeof(item));
  922. item.cbSize = sizeof(item);
  923. item.fMask = MIIM_DATA;
  924. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  925. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  926. if (item_data) {
  927. item_data->state = p_state;
  928. }
  929. }
  930. }
  931. void NativeMenuWindows::set_item_max_states(const RID &p_rid, int p_idx, int p_max_states) {
  932. ERR_FAIL_COND(p_idx < 0);
  933. const MenuData *md = menus.get_or_null(p_rid);
  934. ERR_FAIL_NULL(md);
  935. int count = GetMenuItemCount(md->menu);
  936. ERR_FAIL_COND(p_idx >= count);
  937. MENUITEMINFOW item;
  938. ZeroMemory(&item, sizeof(item));
  939. item.cbSize = sizeof(item);
  940. item.fMask = MIIM_DATA;
  941. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  942. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  943. if (item_data) {
  944. item_data->max_states = p_max_states;
  945. }
  946. }
  947. }
  948. void NativeMenuWindows::set_item_icon(const RID &p_rid, int p_idx, const Ref<Texture2D> &p_icon) {
  949. ERR_FAIL_COND(p_idx < 0);
  950. const MenuData *md = menus.get_or_null(p_rid);
  951. ERR_FAIL_NULL(md);
  952. int count = GetMenuItemCount(md->menu);
  953. ERR_FAIL_COND(p_idx >= count);
  954. MENUITEMINFOW item;
  955. ZeroMemory(&item, sizeof(item));
  956. item.cbSize = sizeof(item);
  957. item.fMask = MIIM_DATA | MIIM_BITMAP;
  958. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  959. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  960. if (item_data) {
  961. if (item_data->bmp) {
  962. DeleteObject(item_data->bmp);
  963. }
  964. if (p_icon.is_valid() && p_icon->get_width() > 0 && p_icon->get_height() > 0 && p_icon->get_image().is_valid()) {
  965. item_data->img = p_icon->get_image();
  966. item_data->img = item_data->img->duplicate();
  967. if (item_data->img->is_compressed()) {
  968. item_data->img->decompress();
  969. }
  970. item_data->bmp = _make_bitmap(item_data->img);
  971. } else {
  972. item_data->img = Ref<Image>();
  973. item_data->bmp = nullptr;
  974. }
  975. item.hbmpItem = item_data->bmp;
  976. SetMenuItemInfoW(md->menu, p_idx, true, &item);
  977. }
  978. }
  979. }
  980. void NativeMenuWindows::set_item_indentation_level(const RID &p_rid, int p_idx, int p_level) {
  981. // Not supported.
  982. }
  983. int NativeMenuWindows::get_item_count(const RID &p_rid) const {
  984. const MenuData *md = menus.get_or_null(p_rid);
  985. ERR_FAIL_NULL_V(md, 0);
  986. return GetMenuItemCount(md->menu);
  987. }
  988. bool NativeMenuWindows::is_system_menu(const RID &p_rid) const {
  989. return false;
  990. }
  991. void NativeMenuWindows::remove_item(const RID &p_rid, int p_idx) {
  992. ERR_FAIL_COND(p_idx < 0);
  993. const MenuData *md = menus.get_or_null(p_rid);
  994. ERR_FAIL_NULL(md);
  995. int count = GetMenuItemCount(md->menu);
  996. ERR_FAIL_COND(p_idx >= count);
  997. MENUITEMINFOW item;
  998. ZeroMemory(&item, sizeof(item));
  999. item.cbSize = sizeof(item);
  1000. item.fMask = MIIM_DATA;
  1001. if (GetMenuItemInfoW(md->menu, p_idx, true, &item)) {
  1002. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  1003. if (item_data) {
  1004. if (item_data->bmp) {
  1005. DeleteObject(item_data->bmp);
  1006. }
  1007. memdelete(item_data);
  1008. }
  1009. }
  1010. RemoveMenu(md->menu, p_idx, MF_BYPOSITION);
  1011. }
  1012. void NativeMenuWindows::clear(const RID &p_rid) {
  1013. const MenuData *md = menus.get_or_null(p_rid);
  1014. ERR_FAIL_NULL(md);
  1015. MENUITEMINFOW item;
  1016. int count = GetMenuItemCount(md->menu);
  1017. for (int i = 0; i < count; i++) {
  1018. ZeroMemory(&item, sizeof(item));
  1019. item.cbSize = sizeof(item);
  1020. item.fMask = MIIM_DATA;
  1021. if (GetMenuItemInfoW(md->menu, 0, true, &item)) {
  1022. MenuItemData *item_data = (MenuItemData *)item.dwItemData;
  1023. if (item_data) {
  1024. if (item_data->bmp) {
  1025. DeleteObject(item_data->bmp);
  1026. }
  1027. memdelete(item_data);
  1028. }
  1029. }
  1030. RemoveMenu(md->menu, 0, MF_BYPOSITION);
  1031. }
  1032. }
  1033. NativeMenuWindows::NativeMenuWindows() {}
  1034. NativeMenuWindows::~NativeMenuWindows() {}