2014-02-10 01:10:30 +00:00
/**************************************************************************/
/* popup_menu.cpp */
/**************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
/* https://godotengine.org */
/**************************************************************************/
/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
/* */
/* Permission is hereby granted, free of charge, to any person obtaining */
/* a copy of this software and associated documentation files (the */
/* "Software"), to deal in the Software without restriction, including */
/* without limitation the rights to use, copy, modify, merge, publish, */
/* distribute, sublicense, and/or sell copies of the Software, and to */
/* permit persons to whom the Software is furnished to do so, subject to */
/* the following conditions: */
/* */
/* The above copyright notice and this permission notice shall be */
/* included in all copies or substantial portions of the Software. */
/* */
/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
/**************************************************************************/
2018-01-04 23:50:27 +00:00
2014-02-10 01:10:30 +00:00
# include "popup_menu.h"
2020-02-23 21:23:34 +00:00
# include "popup_menu.compat.inc"
2020-02-21 22:26:13 +00:00
2022-02-12 01:46:22 +00:00
# include "core/config/project_settings.h"
2020-04-28 13:19:37 +00:00
# include "core/input/input.h"
2018-09-11 16:13:45 +00:00
# include "core/os/keyboard.h"
2019-05-11 16:32:53 +00:00
# include "core/os/os.h"
2020-11-07 22:33:38 +00:00
# include "core/string/print_string.h"
# include "core/string/translation.h"
2022-08-01 09:28:16 +00:00
# include "scene/gui/menu_bar.h"
2023-09-08 19:00:10 +00:00
# include "scene/theme/theme_db.h"
2014-02-10 01:10:30 +00:00
2024-01-19 17:41:01 +00:00
HashMap < NativeMenu : : SystemMenus , PopupMenu * > PopupMenu : : system_menus ;
2023-10-15 17:52:56 +00:00
2023-10-06 06:36:42 +00:00
bool PopupMenu : : _set_item_accelerator ( int p_index , const Ref < InputEventKey > & p_ie ) {
2024-01-19 17:41:01 +00:00
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
2023-10-06 06:36:42 +00:00
if ( p_ie - > get_physical_keycode ( ) = = Key : : NONE & & p_ie - > get_keycode ( ) = = Key : : NONE & & p_ie - > get_key_label ( ) ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , p_index , p_ie - > get_key_label_with_modifiers ( ) ) ;
2023-10-06 06:36:42 +00:00
return true ;
} else if ( p_ie - > get_keycode ( ) ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , p_index , p_ie - > get_keycode_with_modifiers ( ) ) ;
2023-10-06 06:36:42 +00:00
return true ;
} else if ( p_ie - > get_physical_keycode ( ) ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , p_index , DisplayServer : : get_singleton ( ) - > keyboard_get_keycode_from_physical ( p_ie - > get_physical_keycode_with_modifiers ( ) ) ) ;
2023-10-06 06:36:42 +00:00
return true ;
}
return false ;
}
2024-02-11 11:09:55 +00:00
void PopupMenu : : _set_item_checkable_type ( int p_index , int p_checkable_type ) {
switch ( p_checkable_type ) {
case Item : : CHECKABLE_TYPE_NONE : {
set_item_as_checkable ( p_index , false ) ;
} break ;
case Item : : CHECKABLE_TYPE_CHECK_BOX : {
set_item_as_checkable ( p_index , true ) ;
} break ;
case Item : : CHECKABLE_TYPE_RADIO_BUTTON : {
set_item_as_radio_checkable ( p_index , true ) ;
} break ;
}
}
int PopupMenu : : _get_item_checkable_type ( int p_index ) const {
ERR_FAIL_INDEX_V ( p_index , items . size ( ) , Item : : CHECKABLE_TYPE_NONE ) ;
return items [ p_index ] . checkable_type ;
}
2024-01-19 17:41:01 +00:00
RID PopupMenu : : bind_global_menu ( ) {
2023-07-11 08:17:35 +00:00
# ifdef TOOLS_ENABLED
if ( is_part_of_edited_scene ( ) ) {
2024-01-19 17:41:01 +00:00
return RID ( ) ;
2023-07-11 08:17:35 +00:00
}
# endif
2024-03-07 18:42:24 +00:00
if ( ! NativeMenu : : get_singleton ( ) - > has_feature ( NativeMenu : : FEATURE_POPUP_MENU ) ) {
2024-01-19 17:41:01 +00:00
return RID ( ) ;
2023-07-11 08:17:35 +00:00
}
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
return global_menu ; // Already bound;
2023-07-11 08:17:35 +00:00
}
2024-01-19 17:41:01 +00:00
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
2024-03-15 07:18:48 +00:00
if ( system_menu_id ! = NativeMenu : : INVALID_MENU_ID & & nmenu - > has_system_menu ( system_menu_id ) ) {
2024-01-19 17:41:01 +00:00
if ( system_menus . has ( system_menu_id ) ) {
WARN_PRINT ( vformat ( " Attempting to bind PopupMenu to the system menu %s, but another menu is already bound to it. This menu: %s, current menu: %s " , nmenu - > get_system_menu_name ( system_menu_id ) , get_description ( ) , system_menus [ system_menu_id ] - > get_description ( ) ) ) ;
global_menu = nmenu - > create_menu ( ) ;
2023-10-15 17:52:56 +00:00
} else {
2024-01-19 17:41:01 +00:00
system_menus [ system_menu_id ] = this ;
system_menu = nmenu - > get_system_menu ( system_menu_id ) ;
global_menu = system_menu ;
2023-10-15 17:52:56 +00:00
}
2024-01-19 17:41:01 +00:00
} else {
global_menu = nmenu - > create_menu ( ) ;
2023-10-15 17:52:56 +00:00
}
2024-03-07 18:42:24 +00:00
nmenu - > set_interface_direction ( global_menu , control - > is_layout_rtl ( ) ) ;
2024-01-19 17:41:01 +00:00
nmenu - > set_popup_open_callback ( global_menu , callable_mp ( this , & PopupMenu : : _about_to_popup ) ) ;
nmenu - > set_popup_close_callback ( global_menu , callable_mp ( this , & PopupMenu : : _about_to_close ) ) ;
2023-07-11 08:17:35 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
Item & item = items . write [ i ] ;
if ( item . separator ) {
2024-01-19 17:41:01 +00:00
nmenu - > add_separator ( global_menu ) ;
2023-07-11 08:17:35 +00:00
} else {
2024-01-19 17:41:01 +00:00
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , item . shortcut_is_global ? callable_mp ( this , & PopupMenu : : activate_item ) : Callable ( ) , i ) ;
2023-11-28 16:33:04 +00:00
if ( item . submenu ) {
2024-01-19 17:41:01 +00:00
RID submenu_rid = item . submenu - > bind_global_menu ( ) ;
nmenu - > set_item_submenu ( global_menu , index , submenu_rid ) ;
2023-11-28 16:33:04 +00:00
item . submenu_bound = true ;
2023-07-11 08:17:35 +00:00
}
if ( item . checkable_type = = Item : : CHECKABLE_TYPE_CHECK_BOX ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_checkable ( global_menu , index , true ) ;
2023-07-11 08:17:35 +00:00
} else if ( item . checkable_type = = Item : : CHECKABLE_TYPE_RADIO_BUTTON ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_radio_checkable ( global_menu , index , true ) ;
2023-07-11 08:17:35 +00:00
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_checked ( global_menu , index , item . checked ) ;
nmenu - > set_item_disabled ( global_menu , index , item . disabled ) ;
nmenu - > set_item_max_states ( global_menu , index , item . max_states ) ;
nmenu - > set_item_icon ( global_menu , index , item . icon ) ;
nmenu - > set_item_state ( global_menu , index , item . state ) ;
nmenu - > set_item_indentation_level ( global_menu , index , item . indent ) ;
nmenu - > set_item_tooltip ( global_menu , index , item . tooltip ) ;
2023-07-11 08:17:35 +00:00
if ( ! item . shortcut_is_disabled & & item . shortcut . is_valid ( ) & & item . shortcut - > has_valid_event ( ) ) {
Array events = item . shortcut - > get_events ( ) ;
for ( int j = 0 ; j < events . size ( ) ; j + + ) {
Ref < InputEventKey > ie = events [ j ] ;
2023-10-06 06:36:42 +00:00
if ( ie . is_valid ( ) & & _set_item_accelerator ( index , ie ) ) {
2023-07-11 08:17:35 +00:00
break ;
}
}
} else if ( item . accel ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , index , item . accel ) ;
2023-07-11 08:17:35 +00:00
}
}
}
2024-01-19 17:41:01 +00:00
return global_menu ;
2023-07-11 08:17:35 +00:00
}
void PopupMenu : : unbind_global_menu ( ) {
2024-01-19 17:41:01 +00:00
if ( global_menu . is_null ( ) ) {
2023-07-11 08:17:35 +00:00
return ;
}
2024-01-19 17:41:01 +00:00
if ( global_menu = = system_menu & & system_menus [ system_menu_id ] = = this ) {
system_menus . erase ( system_menu_id ) ;
2023-10-15 17:52:56 +00:00
}
2023-07-11 08:17:35 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
Item & item = items . write [ i ] ;
2023-11-28 16:33:04 +00:00
if ( item . submenu ) {
item . submenu - > unbind_global_menu ( ) ;
2023-07-11 08:17:35 +00:00
item . submenu_bound = false ;
}
}
2024-01-19 17:41:01 +00:00
if ( system_menu ! = global_menu ) {
NativeMenu : : get_singleton ( ) - > free_menu ( global_menu ) ;
} else {
NativeMenu : : get_singleton ( ) - > clear ( global_menu ) ;
}
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
system_menu = RID ( ) ;
global_menu = RID ( ) ;
2023-07-11 08:17:35 +00:00
}
2023-10-15 17:52:56 +00:00
bool PopupMenu : : is_system_menu ( ) const {
2024-01-19 17:41:01 +00:00
return ( global_menu = = system_menu ) & & ( system_menu_id ! = NativeMenu : : INVALID_MENU_ID ) ;
2023-10-15 17:52:56 +00:00
}
2024-01-19 17:41:01 +00:00
void PopupMenu : : set_system_menu ( NativeMenu : : SystemMenus p_system_menu_id ) {
if ( is_inside_tree ( ) & & system_menu_id ! = NativeMenu : : INVALID_MENU_ID ) {
2023-10-15 17:52:56 +00:00
unbind_global_menu ( ) ;
}
2024-01-19 17:41:01 +00:00
system_menu_id = p_system_menu_id ;
if ( is_inside_tree ( ) & & system_menu_id ! = NativeMenu : : INVALID_MENU_ID ) {
2023-10-15 17:52:56 +00:00
bind_global_menu ( ) ;
}
}
2024-01-19 17:41:01 +00:00
NativeMenu : : SystemMenus PopupMenu : : get_system_menu ( ) const {
return system_menu_id ;
2023-10-15 17:52:56 +00:00
}
2020-09-03 11:22:16 +00:00
String PopupMenu : : _get_accel_text ( const Item & p_item ) const {
if ( p_item . shortcut . is_valid ( ) ) {
return p_item . shortcut - > get_as_text ( ) ;
2021-08-13 21:31:57 +00:00
} else if ( p_item . accel ! = Key : : NONE ) {
2020-09-03 11:22:16 +00:00
return keycode_get_string ( p_item . accel ) ;
2020-05-14 14:41:43 +00:00
}
2016-06-05 00:31:29 +00:00
return String ( ) ;
2014-02-10 01:10:30 +00:00
}
2023-05-24 13:58:50 +00:00
Size2 PopupMenu : : _get_item_icon_size ( int p_idx ) const {
const PopupMenu : : Item & item = items [ p_idx ] ;
2023-03-31 19:17:59 +00:00
Size2 icon_size = item . get_icon_size ( ) ;
int max_width = 0 ;
if ( theme_cache . icon_max_width > 0 ) {
max_width = theme_cache . icon_max_width ;
}
if ( item . icon_max_width > 0 & & ( max_width = = 0 | | item . icon_max_width < max_width ) ) {
max_width = item . icon_max_width ;
}
if ( max_width > 0 & & icon_size . width > max_width ) {
icon_size . height = icon_size . height * max_width / icon_size . width ;
icon_size . width = max_width ;
}
return icon_size ;
}
2020-03-12 12:37:40 +00:00
Size2 PopupMenu : : _get_contents_minimum_size ( ) const {
2024-02-23 13:51:47 +00:00
Size2 minsize = theme_cache . panel_style - > get_minimum_size ( ) ;
minsize . width + = scroll_container - > get_v_scroll_bar ( ) - > get_size ( ) . width ;
2016-03-08 23:00:52 +00:00
2021-02-09 17:24:36 +00:00
float max_w = 0.0 ;
float icon_w = 0.0 ;
2022-09-01 10:38:08 +00:00
int check_w = MAX ( theme_cache . checked - > get_width ( ) , theme_cache . radio_checked - > get_width ( ) ) + theme_cache . h_separation ;
2014-02-10 01:10:30 +00:00
int accel_max_w = 0 ;
2019-03-18 13:10:19 +00:00
bool has_check = false ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2022-09-29 09:53:28 +00:00
Size2 item_size ;
2022-10-02 04:55:40 +00:00
const_cast < PopupMenu * > ( this ) - > _shape_item ( i ) ;
2020-08-29 12:05:59 +00:00
2023-03-31 19:17:59 +00:00
Size2 icon_size = _get_item_icon_size ( i ) ;
2022-09-29 09:53:28 +00:00
item_size . height = _get_item_height ( i ) ;
2020-08-29 12:05:59 +00:00
icon_w = MAX ( icon_size . width , icon_w ) ;
2016-03-08 23:00:52 +00:00
2022-09-29 09:53:28 +00:00
item_size . width + = items [ i ] . indent * theme_cache . indent ;
2016-09-17 22:01:11 +00:00
2022-02-06 18:19:04 +00:00
if ( items [ i ] . checkable_type & & ! items [ i ] . separator ) {
2019-03-18 13:10:19 +00:00
has_check = true ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2022-09-29 09:53:28 +00:00
item_size . width + = items [ i ] . text_buf - > get_size ( ) . x ;
item_size . height + = theme_cache . v_separation ;
2014-02-10 01:10:30 +00:00
2021-08-13 21:31:57 +00:00
if ( items [ i ] . accel ! = Key : : NONE | | ( items [ i ] . shortcut . is_valid ( ) & & items [ i ] . shortcut - > has_valid_event ( ) ) ) {
2022-09-01 10:38:08 +00:00
int accel_w = theme_cache . h_separation * 2 ;
2020-09-03 11:22:16 +00:00
accel_w + = items [ i ] . accel_text_buf - > get_size ( ) . x ;
2014-02-10 01:10:30 +00:00
accel_max_w = MAX ( accel_w , accel_max_w ) ;
}
2023-11-28 16:33:04 +00:00
if ( items [ i ] . submenu ) {
2022-09-29 09:53:28 +00:00
item_size . width + = theme_cache . submenu - > get_width ( ) ;
2020-05-14 14:41:43 +00:00
}
2019-03-18 13:10:19 +00:00
2022-09-29 09:53:28 +00:00
max_w = MAX ( max_w , item_size . width ) ;
2017-05-14 11:42:42 +00:00
2022-09-29 09:53:28 +00:00
minsize . height + = item_size . height ;
2014-02-10 01:10:30 +00:00
}
2016-03-08 23:00:52 +00:00
2022-09-01 10:38:08 +00:00
int item_side_padding = theme_cache . item_start_padding + theme_cache . item_end_padding ;
2021-02-04 13:03:36 +00:00
minsize . width + = max_w + icon_w + accel_max_w + item_side_padding ;
2020-05-14 14:41:43 +00:00
if ( has_check ) {
2019-05-01 05:30:56 +00:00
minsize . width + = check_w ;
2020-05-14 14:41:43 +00:00
}
2016-03-08 23:00:52 +00:00
2020-09-06 03:59:40 +00:00
if ( is_inside_tree ( ) ) {
int height_limit = get_usable_parent_rect ( ) . size . height ;
if ( minsize . height > height_limit ) {
minsize . height = height_limit ;
}
2020-08-29 12:05:59 +00:00
}
2024-04-24 15:42:50 +00:00
minsize . height = Math : : ceil ( minsize . height ) ; // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing.
2014-02-10 01:10:30 +00:00
return minsize ;
}
2023-05-24 13:58:50 +00:00
int PopupMenu : : _get_item_height ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , 0 ) ;
2021-02-16 03:30:57 +00:00
2023-05-24 13:58:50 +00:00
Size2 icon_size = _get_item_icon_size ( p_idx ) ;
2023-03-31 19:17:59 +00:00
int icon_height = icon_size . height ;
2023-05-24 13:58:50 +00:00
if ( items [ p_idx ] . checkable_type & & ! items [ p_idx ] . separator ) {
2022-09-01 10:38:08 +00:00
icon_height = MAX ( icon_height , MAX ( theme_cache . checked - > get_height ( ) , theme_cache . radio_checked - > get_height ( ) ) ) ;
2021-02-16 03:30:57 +00:00
}
2023-05-24 13:58:50 +00:00
int text_height = items [ p_idx ] . text_buf - > get_size ( ) . height ;
if ( text_height = = 0 & & ! items [ p_idx ] . separator ) {
2022-09-01 10:38:08 +00:00
text_height = theme_cache . font - > get_height ( theme_cache . font_size ) ;
2021-02-16 03:30:57 +00:00
}
int separator_height = 0 ;
2023-05-24 13:58:50 +00:00
if ( items [ p_idx ] . separator ) {
2022-09-01 10:38:08 +00:00
separator_height = MAX ( theme_cache . separator_style - > get_minimum_size ( ) . height , MAX ( theme_cache . labeled_separator_left - > get_minimum_size ( ) . height , theme_cache . labeled_separator_right - > get_minimum_size ( ) . height ) ) ;
2021-02-16 03:30:57 +00:00
}
return MAX ( separator_height , MAX ( text_height , icon_height ) ) ;
}
2020-08-29 12:05:59 +00:00
int PopupMenu : : _get_items_total_height ( ) const {
// Get total height of all items by taking max of icon height and font height
int items_total_height = 0 ;
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2022-09-01 10:38:08 +00:00
items_total_height + = _get_item_height ( i ) + theme_cache . v_separation ;
2020-08-29 12:05:59 +00:00
}
2023-12-14 01:22:08 +00:00
return items_total_height ;
2020-08-29 12:05:59 +00:00
}
2014-02-10 01:10:30 +00:00
int PopupMenu : : _get_mouse_over ( const Point2 & p_over ) const {
2023-12-27 16:19:52 +00:00
float win_scale = get_content_scale_factor ( ) ;
if ( p_over . x < 0 | | p_over . x > = get_size ( ) . width * win_scale | | p_over . y < theme_cache . panel_style - > get_margin ( Side : : SIDE_TOP ) * win_scale ) {
2014-02-10 01:10:30 +00:00
return - 1 ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2023-12-27 16:19:52 +00:00
Point2 ofs = Point2 ( 0 , theme_cache . v_separation * 0.5 ) * win_scale ;
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2023-12-27 16:19:52 +00:00
ofs . y + = i > 0 ? ( float ) theme_cache . v_separation * win_scale : ( float ) theme_cache . v_separation * win_scale * 0.5 ;
ofs . y + = _get_item_height ( i ) * win_scale ;
if ( p_over . y - control - > get_position ( ) . y * win_scale < ofs . y ) {
2014-02-10 01:10:30 +00:00
return i ;
2016-03-08 23:00:52 +00:00
}
2014-02-10 01:10:30 +00:00
}
2016-03-08 23:00:52 +00:00
2014-02-10 01:10:30 +00:00
return - 1 ;
}
2022-04-13 06:58:38 +00:00
void PopupMenu : : _activate_submenu ( int p_over , bool p_by_keyboard ) {
2023-11-28 16:33:04 +00:00
Popup * submenu_popup = items [ p_over ] . submenu ;
2020-08-29 12:05:59 +00:00
if ( submenu_popup - > is_visible ( ) ) {
2022-01-20 11:44:27 +00:00
return ; // Already visible.
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-08-29 12:05:59 +00:00
Point2 this_pos = get_position ( ) ;
Rect2 this_rect ( this_pos , get_size ( ) ) ;
float scroll_offset = control - > get_position ( ) . y ;
2023-12-27 16:19:52 +00:00
float scaled_ofs_cache = items [ p_over ] . _ofs_cache * get_content_scale_factor ( ) ;
float scaled_height_cache = items [ p_over ] . _height_cache * get_content_scale_factor ( ) ;
2020-08-29 12:05:59 +00:00
2022-03-05 23:57:42 +00:00
submenu_popup - > reset_size ( ) ; // Shrink the popup size to its contents.
2020-08-29 12:05:59 +00:00
Size2 submenu_size = submenu_popup - > get_size ( ) ;
2022-02-07 02:07:08 +00:00
Point2 submenu_pos ;
2020-09-03 11:22:16 +00:00
if ( control - > is_layout_rtl ( ) ) {
2023-12-27 16:19:52 +00:00
submenu_pos = this_pos + Point2 ( - submenu_size . width , scaled_ofs_cache + scroll_offset - theme_cache . v_separation / 2 ) ;
2020-09-03 11:22:16 +00:00
} else {
2023-12-27 16:19:52 +00:00
submenu_pos = this_pos + Point2 ( this_rect . size . width , scaled_ofs_cache + scroll_offset - theme_cache . v_separation / 2 ) ;
2020-09-03 11:22:16 +00:00
}
2020-08-29 12:05:59 +00:00
2022-02-07 02:07:08 +00:00
// Fix pos if going outside parent rect.
2020-09-03 11:22:16 +00:00
if ( submenu_pos . x < get_parent_rect ( ) . position . x ) {
submenu_pos . x = this_pos . x + submenu_size . width ;
}
2021-08-31 15:43:35 +00:00
if ( submenu_pos . x + submenu_size . width > get_parent_rect ( ) . position . x + get_parent_rect ( ) . size . width ) {
2020-08-29 12:05:59 +00:00
submenu_pos . x = this_pos . x - submenu_size . width ;
2020-05-14 14:41:43 +00:00
}
2015-11-22 14:53:22 +00:00
2020-08-29 12:05:59 +00:00
submenu_popup - > set_position ( submenu_pos ) ;
PopupMenu * submenu_pum = Object : : cast_to < PopupMenu > ( submenu_popup ) ;
2022-01-20 11:44:27 +00:00
if ( ! submenu_pum ) {
submenu_popup - > popup ( ) ;
return ;
}
2022-04-13 06:58:38 +00:00
submenu_pum - > activated_by_keyboard = p_by_keyboard ;
2022-08-27 16:32:45 +00:00
// If not triggered by the mouse, start the popup with its first enabled item focused.
if ( p_by_keyboard ) {
for ( int i = 0 ; i < submenu_pum - > get_item_count ( ) ; i + + ) {
if ( ! submenu_pum - > is_item_disabled ( i ) ) {
2022-09-06 13:51:14 +00:00
submenu_pum - > set_focused_item ( i ) ;
2022-08-27 16:32:45 +00:00
break ;
}
}
2022-01-20 11:44:27 +00:00
}
submenu_pum - > popup ( ) ;
// Set autohide areas.
2022-02-24 09:21:23 +00:00
Rect2 safe_area = this_rect ;
2023-12-27 16:19:52 +00:00
safe_area . position . y + = scaled_ofs_cache + scroll_offset + theme_cache . panel_style - > get_offset ( ) . height - theme_cache . v_separation / 2 ;
safe_area . size . y = scaled_height_cache + theme_cache . v_separation ;
2023-06-20 13:50:44 +00:00
Viewport * vp = submenu_popup - > get_embedder ( ) ;
if ( vp ) {
vp - > subwindow_set_popup_safe_rect ( submenu_popup , safe_area ) ;
} else {
DisplayServer : : get_singleton ( ) - > window_set_popup_safe_rect ( submenu_popup - > get_window_id ( ) , safe_area ) ;
}
2022-02-24 09:21:23 +00:00
2022-01-20 11:44:27 +00:00
// Make the position of the parent popup relative to submenu popup.
this_rect . position = this_rect . position - submenu_pum - > get_position ( ) ;
// Autohide area above the submenu item.
submenu_pum - > clear_autohide_areas ( ) ;
2023-12-27 16:19:52 +00:00
submenu_pum - > add_autohide_area ( Rect2 ( this_rect . position . x , this_rect . position . y , this_rect . size . x , scaled_ofs_cache + scroll_offset + theme_cache . panel_style - > get_offset ( ) . height - theme_cache . v_separation / 2 ) ) ;
2022-01-20 11:44:27 +00:00
// If there is an area below the submenu item, add an autohide area there.
2023-12-27 16:19:52 +00:00
if ( scaled_ofs_cache + scaled_height_cache + scroll_offset < = control - > get_size ( ) . height ) {
int from = scaled_ofs_cache + scaled_height_cache + scroll_offset + theme_cache . v_separation / 2 + theme_cache . panel_style - > get_offset ( ) . height ;
2022-01-20 11:44:27 +00:00
submenu_pum - > add_autohide_area ( Rect2 ( this_rect . position . x , this_rect . position . y + from , this_rect . size . x , this_rect . size . y - from ) ) ;
2014-02-10 01:10:30 +00:00
}
}
2022-06-09 08:59:01 +00:00
void PopupMenu : : _parent_focused ( ) {
if ( is_embedded ( ) ) {
Point2 mouse_pos_adjusted ;
Window * window_parent = Object : : cast_to < Window > ( get_parent ( ) - > get_viewport ( ) ) ;
while ( window_parent ) {
if ( ! window_parent - > is_embedded ( ) ) {
mouse_pos_adjusted + = window_parent - > get_position ( ) ;
break ;
}
window_parent = Object : : cast_to < Window > ( window_parent - > get_parent ( ) - > get_viewport ( ) ) ;
}
2023-06-20 13:50:44 +00:00
Rect2 safe_area = get_embedder ( ) - > subwindow_get_popup_safe_rect ( this ) ;
2022-06-09 08:59:01 +00:00
Point2 pos = DisplayServer : : get_singleton ( ) - > mouse_get_position ( ) - mouse_pos_adjusted ;
if ( safe_area = = Rect2i ( ) | | ! safe_area . has_point ( pos ) ) {
Popup : : _parent_focused ( ) ;
} else {
grab_focus ( ) ;
}
}
}
2014-02-10 01:10:30 +00:00
void PopupMenu : : _submenu_timeout ( ) {
2020-05-14 14:41:43 +00:00
if ( mouse_over = = submenu_over ) {
2014-02-10 01:10:30 +00:00
_activate_submenu ( mouse_over ) ;
2020-05-14 14:41:43 +00:00
}
2017-12-06 19:27:21 +00:00
submenu_over = - 1 ;
2014-02-10 01:10:30 +00:00
}
2023-09-01 06:40:35 +00:00
void PopupMenu : : _input_from_window ( const Ref < InputEvent > & p_event ) {
if ( p_event . is_valid ( ) ) {
_input_from_window_internal ( p_event ) ;
} else {
WARN_PRINT_ONCE ( " PopupMenu has received an invalid InputEvent. Consider filtering invalid events out. " ) ;
}
Popup : : _input_from_window ( p_event ) ;
}
2021-04-05 06:52:21 +00:00
2023-09-01 06:40:35 +00:00
void PopupMenu : : _input_from_window_internal ( const Ref < InputEvent > & p_event ) {
2022-08-27 21:13:27 +00:00
if ( ! items . is_empty ( ) ) {
2023-02-09 16:25:56 +00:00
Input * input = Input : : get_singleton ( ) ;
Ref < InputEventJoypadMotion > joypadmotion_event = p_event ;
Ref < InputEventJoypadButton > joypadbutton_event = p_event ;
bool is_joypad_event = ( joypadmotion_event . is_valid ( ) | | joypadbutton_event . is_valid ( ) ) ;
2022-09-24 08:01:02 +00:00
if ( p_event - > is_action ( " ui_down " , true ) & & p_event - > is_pressed ( ) ) {
2023-02-09 16:25:56 +00:00
if ( is_joypad_event ) {
if ( ! input - > is_action_just_pressed ( " ui_down " , true ) ) {
return ;
}
set_process_internal ( true ) ;
}
2022-08-27 21:13:27 +00:00
int search_from = mouse_over + 1 ;
if ( search_from > = items . size ( ) ) {
search_from = 0 ;
2017-12-31 03:42:25 +00:00
}
2020-12-10 12:32:02 +00:00
2022-08-27 21:13:27 +00:00
bool match_found = false ;
for ( int i = search_from ; i < items . size ( ) ; i + + ) {
2020-12-10 12:32:02 +00:00
if ( ! items [ i ] . separator & & ! items [ i ] . disabled ) {
mouse_over = i ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " id_focused " ) , i ) ;
2022-02-06 02:47:00 +00:00
scroll_to_item ( i ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-12-10 12:32:02 +00:00
set_input_as_handled ( ) ;
2022-08-27 21:13:27 +00:00
match_found = true ;
2020-12-10 12:32:02 +00:00
break ;
}
}
2017-12-16 22:54:44 +00:00
2022-08-27 21:13:27 +00:00
if ( ! match_found ) {
// If the last item is not selectable, try re-searching from the start.
for ( int i = 0 ; i < search_from ; i + + ) {
if ( ! items [ i ] . separator & & ! items [ i ] . disabled ) {
mouse_over = i ;
emit_signal ( SNAME ( " id_focused " ) , i ) ;
scroll_to_item ( i ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2022-08-27 21:13:27 +00:00
set_input_as_handled ( ) ;
break ;
}
}
}
2022-09-24 08:01:02 +00:00
} else if ( p_event - > is_action ( " ui_up " , true ) & & p_event - > is_pressed ( ) ) {
2023-02-09 16:25:56 +00:00
if ( is_joypad_event ) {
if ( ! input - > is_action_just_pressed ( " ui_up " , true ) ) {
return ;
}
set_process_internal ( true ) ;
}
2022-08-27 21:13:27 +00:00
int search_from = mouse_over - 1 ;
if ( search_from < 0 ) {
search_from = items . size ( ) - 1 ;
2017-12-31 03:42:25 +00:00
}
2020-12-10 12:32:02 +00:00
2022-08-27 21:13:27 +00:00
bool match_found = false ;
for ( int i = search_from ; i > = 0 ; i - - ) {
2020-12-10 12:32:02 +00:00
if ( ! items [ i ] . separator & & ! items [ i ] . disabled ) {
mouse_over = i ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " id_focused " ) , i ) ;
2022-02-06 02:47:00 +00:00
scroll_to_item ( i ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-12-10 12:32:02 +00:00
set_input_as_handled ( ) ;
2022-08-27 21:13:27 +00:00
match_found = true ;
2020-12-10 12:32:02 +00:00
break ;
}
}
2022-08-27 21:13:27 +00:00
if ( ! match_found ) {
// If the first item is not selectable, try re-searching from the end.
for ( int i = items . size ( ) - 1 ; i > = search_from ; i - - ) {
if ( ! items [ i ] . separator & & ! items [ i ] . disabled ) {
mouse_over = i ;
emit_signal ( SNAME ( " id_focused " ) , i ) ;
scroll_to_item ( i ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2022-08-27 21:13:27 +00:00
set_input_as_handled ( ) ;
break ;
}
}
2022-08-01 09:28:16 +00:00
}
2022-09-24 08:01:02 +00:00
} else if ( p_event - > is_action ( " ui_left " , true ) & & p_event - > is_pressed ( ) ) {
2022-08-01 09:28:16 +00:00
Node * n = get_parent ( ) ;
2022-08-27 21:13:27 +00:00
if ( n ) {
if ( Object : : cast_to < PopupMenu > ( n ) ) {
hide ( ) ;
set_input_as_handled ( ) ;
} else if ( Object : : cast_to < MenuBar > ( n ) ) {
Object : : cast_to < MenuBar > ( n ) - > gui_input ( p_event ) ;
set_input_as_handled ( ) ;
return ;
}
2022-08-01 09:28:16 +00:00
}
2022-09-24 08:01:02 +00:00
} else if ( p_event - > is_action ( " ui_right " , true ) & & p_event - > is_pressed ( ) ) {
2023-11-28 16:33:04 +00:00
if ( mouse_over > = 0 & & mouse_over < items . size ( ) & & ! items [ mouse_over ] . separator & & items [ mouse_over ] . submenu & & submenu_over ! = mouse_over ) {
2022-04-13 06:58:38 +00:00
_activate_submenu ( mouse_over , true ) ;
2022-08-27 21:13:27 +00:00
set_input_as_handled ( ) ;
2017-12-31 03:42:25 +00:00
} else {
2022-08-27 21:13:27 +00:00
Node * n = get_parent ( ) ;
if ( n & & Object : : cast_to < MenuBar > ( n ) ) {
Object : : cast_to < MenuBar > ( n ) - > gui_input ( p_event ) ;
set_input_as_handled ( ) ;
return ;
}
}
2022-09-24 08:01:02 +00:00
} else if ( p_event - > is_action ( " ui_accept " , true ) & & p_event - > is_pressed ( ) ) {
2022-08-27 21:13:27 +00:00
if ( mouse_over > = 0 & & mouse_over < items . size ( ) & & ! items [ mouse_over ] . separator ) {
2023-11-28 16:33:04 +00:00
if ( items [ mouse_over ] . submenu & & submenu_over ! = mouse_over ) {
2022-08-27 21:13:27 +00:00
_activate_submenu ( mouse_over , true ) ;
} else {
activate_item ( mouse_over ) ;
}
set_input_as_handled ( ) ;
2017-12-31 03:42:25 +00:00
}
2017-05-20 15:38:03 +00:00
}
}
2014-02-10 01:10:30 +00:00
2020-08-29 12:05:59 +00:00
// Make an area which does not include v scrollbar, so that items are not activated when dragging scrollbar.
2020-09-01 21:17:46 +00:00
Rect2 item_clickable_area = scroll_container - > get_rect ( ) ;
2021-11-30 16:46:36 +00:00
if ( scroll_container - > get_v_scroll_bar ( ) - > is_visible_in_tree ( ) ) {
2020-09-03 11:22:16 +00:00
if ( is_layout_rtl ( ) ) {
2021-11-30 16:46:36 +00:00
item_clickable_area . position . x + = scroll_container - > get_v_scroll_bar ( ) - > get_size ( ) . width ;
2020-09-03 11:22:16 +00:00
}
2024-02-23 13:51:47 +00:00
item_clickable_area . size . width - = scroll_container - > get_v_scroll_bar ( ) - > get_size ( ) . width ;
2020-09-01 21:17:46 +00:00
}
2023-12-27 16:19:52 +00:00
item_clickable_area . size = item_clickable_area . size * get_content_scale_factor ( ) ;
2020-08-29 12:05:59 +00:00
2024-02-20 09:05:41 +00:00
Ref < InputEventMouseButton > b = p_event ;
2014-02-10 01:10:30 +00:00
2024-02-20 09:05:41 +00:00
if ( b . is_valid ( ) ) {
MouseButton button_idx = b - > get_button_index ( ) ;
2024-02-23 13:51:47 +00:00
// Activate the item on release of either the left mouse button or
// any mouse button held down when the popup was opened.
// This allows for opening the popup and triggering an action in a single mouse click.
if ( button_idx = = MouseButton : : LEFT | | initial_button_mask . has_flag ( mouse_button_to_mask ( button_idx ) ) ) {
if ( b - > is_pressed ( ) ) {
is_scrolling = is_layout_rtl ( ) ? b - > get_position ( ) . x < item_clickable_area . position . x : b - > get_position ( ) . x > item_clickable_area . size . width ;
if ( ! item_clickable_area . has_point ( b - > get_position ( ) ) ) {
return ;
}
_mouse_over_update ( b - > get_position ( ) ) ;
} else {
if ( is_scrolling ) {
is_scrolling = false ;
return ;
}
2024-02-20 09:05:41 +00:00
bool was_during_grabbed_click = during_grabbed_click ;
during_grabbed_click = false ;
initial_button_mask . clear ( ) ;
2024-02-23 13:51:47 +00:00
if ( ! item_clickable_area . has_point ( b - > get_position ( ) ) ) {
return ;
}
2024-02-20 09:05:41 +00:00
// Disable clicks under a time threshold to avoid selection right when opening the popup.
2024-02-23 13:51:47 +00:00
if ( was_during_grabbed_click & & OS : : get_singleton ( ) - > get_ticks_msec ( ) - popup_time_msec < 150 ) {
2024-02-20 09:05:41 +00:00
return ;
}
2016-03-08 23:00:52 +00:00
2024-02-20 09:05:41 +00:00
int over = _get_mouse_over ( b - > get_position ( ) ) ;
if ( over < 0 ) {
if ( ! was_during_grabbed_click ) {
hide ( ) ;
}
return ;
}
2024-01-08 05:07:24 +00:00
2024-02-20 09:05:41 +00:00
if ( items [ over ] . separator | | items [ over ] . disabled ) {
return ;
}
2024-01-08 05:07:24 +00:00
2023-11-28 16:33:04 +00:00
if ( items [ over ] . submenu ) {
2024-02-20 09:05:41 +00:00
_activate_submenu ( over ) ;
return ;
}
activate_item ( over ) ;
}
2024-01-08 05:07:24 +00:00
}
}
2015-01-03 19:52:37 +00:00
2024-02-20 09:05:41 +00:00
Ref < InputEventMouseMotion > m = p_event ;
2017-05-20 15:38:03 +00:00
if ( m . is_valid ( ) ) {
2022-09-02 00:32:33 +00:00
if ( m - > get_velocity ( ) . is_zero_approx ( ) ) {
2022-04-13 06:58:38 +00:00
return ;
}
activated_by_keyboard = false ;
2021-07-24 13:46:25 +00:00
for ( const Rect2 & E : autohide_areas ) {
2021-07-16 03:45:57 +00:00
if ( ! Rect2 ( Point2 ( ) , get_size ( ) ) . has_point ( m - > get_position ( ) ) & & E . has_point ( m - > get_position ( ) ) ) {
2024-04-13 18:10:19 +00:00
// The mouse left the safe area, prepare to close.
2020-03-20 02:32:09 +00:00
_close_pressed ( ) ;
2017-05-20 15:38:03 +00:00
return ;
2015-11-05 14:22:50 +00:00
}
2017-05-20 15:38:03 +00:00
}
2014-02-10 01:10:30 +00:00
2024-04-13 18:10:19 +00:00
if ( ! minimum_lifetime_timer - > is_stopped ( ) ) {
// The mouse left the safe area, but came back again, so cancel the auto-closing.
minimum_lifetime_timer - > stop ( ) ;
}
2021-07-23 15:13:01 +00:00
if ( ! item_clickable_area . has_point ( m - > get_position ( ) ) ) {
return ;
}
2024-02-23 13:51:47 +00:00
_mouse_over_update ( m - > get_position ( ) ) ;
2014-02-10 01:10:30 +00:00
}
2018-01-06 15:21:27 +00:00
2019-05-11 16:32:53 +00:00
Ref < InputEventKey > k = p_event ;
2020-03-30 19:45:56 +00:00
if ( allow_search & & k . is_valid ( ) & & k - > get_unicode ( ) & & k - > is_pressed ( ) ) {
2019-05-11 16:32:53 +00:00
uint64_t now = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
uint64_t diff = now - search_time_msec ;
2022-10-20 13:43:17 +00:00
uint64_t max_interval = uint64_t ( GLOBAL_GET ( " gui/timers/incremental_search_max_interval_msec " ) ) ;
2019-05-11 16:32:53 +00:00
search_time_msec = now ;
if ( diff > max_interval ) {
search_string = " " ;
}
2020-05-14 14:41:43 +00:00
if ( String : : chr ( k - > get_unicode ( ) ) ! = search_string ) {
2019-05-11 16:32:53 +00:00
search_string + = String : : chr ( k - > get_unicode ( ) ) ;
2020-05-14 14:41:43 +00:00
}
2019-05-11 16:32:53 +00:00
for ( int i = mouse_over + 1 ; i < = items . size ( ) ; i + + ) {
if ( i = = items . size ( ) ) {
2020-05-14 14:41:43 +00:00
if ( mouse_over < = 0 ) {
2019-05-11 16:32:53 +00:00
break ;
2020-05-14 14:41:43 +00:00
} else {
2019-05-11 16:32:53 +00:00
i = 0 ;
2020-05-14 14:41:43 +00:00
}
2019-05-11 16:32:53 +00:00
}
2020-05-14 14:41:43 +00:00
if ( i = = mouse_over ) {
2019-05-11 16:32:53 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2019-05-11 16:32:53 +00:00
if ( items [ i ] . text . findn ( search_string ) = = 0 ) {
mouse_over = i ;
2021-07-17 21:22:52 +00:00
emit_signal ( SNAME ( " id_focused " ) , i ) ;
2022-02-06 02:47:00 +00:00
scroll_to_item ( i ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
set_input_as_handled ( ) ;
2019-05-11 16:32:53 +00:00
break ;
}
}
}
2014-02-10 01:10:30 +00:00
}
2024-02-23 13:51:47 +00:00
void PopupMenu : : _mouse_over_update ( const Point2 & p_over ) {
int over = _get_mouse_over ( p_over ) ;
int id = ( over < 0 | | items [ over ] . separator | | items [ over ] . disabled ) ? - 1 : ( items [ over ] . id > = 0 ? items [ over ] . id : over ) ;
if ( id < 0 ) {
mouse_over = - 1 ;
control - > queue_redraw ( ) ;
return ;
}
if ( ! is_scrolling & & items [ over ] . submenu & & submenu_over ! = over ) {
submenu_over = over ;
submenu_timer - > start ( ) ;
}
if ( over ! = mouse_over ) {
mouse_over = over ;
control - > queue_redraw ( ) ;
}
}
2020-08-29 12:05:59 +00:00
void PopupMenu : : _draw_items ( ) {
control - > set_custom_minimum_size ( Size2 ( 0 , _get_items_total_height ( ) ) ) ;
2020-03-12 12:37:40 +00:00
RID ci = control - > get_canvas_item ( ) ;
2020-08-29 12:05:59 +00:00
2021-02-04 13:03:36 +00:00
// Space between the item content and the sides of popup menu.
2020-09-03 11:22:16 +00:00
bool rtl = control - > is_layout_rtl ( ) ;
2022-08-11 12:15:04 +00:00
// In Item::checkable_type enum order (less the non-checkable member), with disabled repeated at the end.
2022-09-01 10:38:08 +00:00
Ref < Texture2D > check [ ] = { theme_cache . checked , theme_cache . radio_checked , theme_cache . checked_disabled , theme_cache . radio_checked_disabled } ;
Ref < Texture2D > uncheck [ ] = { theme_cache . unchecked , theme_cache . radio_unchecked , theme_cache . unchecked_disabled , theme_cache . radio_unchecked_disabled } ;
2020-09-03 11:22:16 +00:00
Ref < Texture2D > submenu ;
if ( rtl ) {
2022-09-01 10:38:08 +00:00
submenu = theme_cache . submenu_mirrored ;
2020-09-03 11:22:16 +00:00
} else {
2022-09-01 10:38:08 +00:00
submenu = theme_cache . submenu ;
2020-09-03 11:22:16 +00:00
}
2024-02-23 13:51:47 +00:00
float display_width = control - > get_size ( ) . width ;
2020-08-29 12:05:59 +00:00
// Find the widest icon and whether any items have a checkbox, and store the offsets for each.
2020-03-12 12:37:40 +00:00
float icon_ofs = 0.0 ;
bool has_check = false ;
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2022-02-06 18:19:04 +00:00
if ( items [ i ] . separator ) {
continue ;
}
2023-03-31 19:17:59 +00:00
Size2 icon_size = _get_item_icon_size ( i ) ;
icon_ofs = MAX ( icon_size . width , icon_ofs ) ;
2018-07-09 12:16:06 +00:00
2020-05-14 14:41:43 +00:00
if ( items [ i ] . checkable_type ) {
2020-03-12 12:37:40 +00:00
has_check = true ;
2020-05-14 14:41:43 +00:00
}
2020-03-12 12:37:40 +00:00
}
2020-05-14 14:41:43 +00:00
if ( icon_ofs > 0.0 ) {
2022-09-01 10:38:08 +00:00
icon_ofs + = theme_cache . h_separation ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2020-03-12 12:37:40 +00:00
float check_ofs = 0.0 ;
2020-05-14 14:41:43 +00:00
if ( has_check ) {
2022-08-11 12:15:04 +00:00
for ( int i = 0 ; i < 4 ; i + + ) {
check_ofs = MAX ( check_ofs , check [ i ] - > get_width ( ) ) ;
check_ofs = MAX ( check_ofs , uncheck [ i ] - > get_width ( ) ) ;
}
2022-09-01 10:38:08 +00:00
check_ofs + = theme_cache . h_separation ;
2020-05-14 14:41:43 +00:00
}
2017-01-09 19:43:44 +00:00
2022-11-14 17:21:06 +00:00
Point2 ofs ;
2020-08-29 12:05:59 +00:00
// Loop through all items and draw each.
2020-03-12 12:37:40 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2021-01-04 08:31:13 +00:00
// For the first item only add half a separation. For all other items, add a whole separation to the offset.
2022-09-01 10:38:08 +00:00
ofs . y + = i > 0 ? theme_cache . v_separation : ( float ) theme_cache . v_separation / 2 ;
2019-03-18 13:10:19 +00:00
2020-09-03 11:22:16 +00:00
_shape_item ( i ) ;
2020-08-29 12:05:59 +00:00
Point2 item_ofs = ofs ;
2023-03-31 19:17:59 +00:00
Size2 icon_size = _get_item_icon_size ( i ) ;
2021-02-16 03:30:57 +00:00
float h = _get_item_height ( i ) ;
2016-03-08 23:00:52 +00:00
2020-03-12 12:37:40 +00:00
if ( i = = mouse_over ) {
2024-02-23 13:51:47 +00:00
theme_cache . hover_style - > draw ( ci , Rect2 ( item_ofs + Point2 ( 0 , - theme_cache . v_separation / 2 ) , Size2 ( display_width , h + theme_cache . v_separation ) ) ) ;
2020-03-12 12:37:40 +00:00
}
2016-03-08 23:00:52 +00:00
2020-03-12 12:37:40 +00:00
String text = items [ i ] . xl_text ;
2016-03-08 23:00:52 +00:00
2020-08-29 12:05:59 +00:00
// Separator
2022-09-01 10:38:08 +00:00
item_ofs . x + = items [ i ] . indent * theme_cache . indent ;
2020-03-12 12:37:40 +00:00
if ( items [ i ] . separator ) {
2023-10-17 18:38:35 +00:00
if ( ! text . is_empty ( ) | | items [ i ] . icon . is_valid ( ) ) {
2022-09-01 10:38:08 +00:00
int content_size = items [ i ] . text_buf - > get_size ( ) . width + theme_cache . h_separation * 2 ;
2023-10-17 18:38:35 +00:00
if ( items [ i ] . icon . is_valid ( ) ) {
2022-09-01 10:38:08 +00:00
content_size + = icon_size . width + theme_cache . h_separation ;
2014-02-10 01:10:30 +00:00
}
2022-02-06 18:19:04 +00:00
int content_center = display_width / 2 ;
int content_left = content_center - content_size / 2 ;
int content_right = content_center + content_size / 2 ;
if ( content_left > item_ofs . x ) {
2023-01-19 16:14:09 +00:00
int sep_h = theme_cache . labeled_separator_left - > get_minimum_size ( ) . height ;
2022-04-13 20:13:29 +00:00
int sep_ofs = Math : : floor ( ( h - sep_h ) / 2.0 ) ;
2022-09-01 10:38:08 +00:00
theme_cache . labeled_separator_left - > draw ( ci , Rect2 ( item_ofs + Point2 ( 0 , sep_ofs ) , Size2 ( MAX ( 0 , content_left - item_ofs . x ) , sep_h ) ) ) ;
2022-02-06 18:19:04 +00:00
}
if ( content_right < display_width ) {
2023-01-19 16:14:09 +00:00
int sep_h = theme_cache . labeled_separator_right - > get_minimum_size ( ) . height ;
2022-04-13 20:13:29 +00:00
int sep_ofs = Math : : floor ( ( h - sep_h ) / 2.0 ) ;
2022-09-01 10:38:08 +00:00
theme_cache . labeled_separator_right - > draw ( ci , Rect2 ( Point2 ( content_right , item_ofs . y + sep_ofs ) , Size2 ( MAX ( 0 , display_width - content_right ) , sep_h ) ) ) ;
2014-02-10 01:10:30 +00:00
}
2020-03-12 12:37:40 +00:00
} else {
2023-01-19 16:14:09 +00:00
int sep_h = theme_cache . separator_style - > get_minimum_size ( ) . height ;
2022-04-13 20:13:29 +00:00
int sep_ofs = Math : : floor ( ( h - sep_h ) / 2.0 ) ;
2022-09-01 10:38:08 +00:00
theme_cache . separator_style - > draw ( ci , Rect2 ( item_ofs + Point2 ( 0 , sep_ofs ) , Size2 ( display_width , sep_h ) ) ) ;
2020-03-12 12:37:40 +00:00
}
}
2016-03-08 23:00:52 +00:00
2023-11-15 17:21:25 +00:00
Color icon_color = items [ i ] . icon_modulate ;
2022-12-19 05:38:08 +00:00
2021-02-04 13:03:36 +00:00
// For non-separator items, add some padding for the content.
2023-10-17 18:38:35 +00:00
if ( ! items [ i ] . separator ) {
item_ofs . x + = theme_cache . item_start_padding ;
}
2021-02-04 13:03:36 +00:00
2020-08-29 12:05:59 +00:00
// Checkboxes
2022-02-06 18:19:04 +00:00
if ( items [ i ] . checkable_type & & ! items [ i ] . separator ) {
2022-08-11 12:15:04 +00:00
int disabled = int ( items [ i ] . disabled ) * 2 ;
Texture2D * icon = ( items [ i ] . checked ? check [ items [ i ] . checkable_type - 1 + disabled ] : uncheck [ items [ i ] . checkable_type - 1 + disabled ] ) . ptr ( ) ;
2020-09-03 11:22:16 +00:00
if ( rtl ) {
icon - > draw ( ci , Size2 ( control - > get_size ( ) . width - item_ofs . x - icon - > get_width ( ) , item_ofs . y ) + Point2 ( 0 , Math : : floor ( ( h - icon - > get_height ( ) ) / 2.0 ) ) , icon_color ) ;
} else {
icon - > draw ( ci , item_ofs + Point2 ( 0 , Math : : floor ( ( h - icon - > get_height ( ) ) / 2.0 ) ) , icon_color ) ;
}
2020-03-12 12:37:40 +00:00
}
2016-03-08 23:00:52 +00:00
2022-02-06 18:19:04 +00:00
int separator_ofs = ( display_width - items [ i ] . text_buf - > get_size ( ) . width ) / 2 ;
2020-08-29 12:05:59 +00:00
// Icon
2023-10-17 18:38:35 +00:00
if ( items [ i ] . icon . is_valid ( ) ) {
2023-03-31 19:17:59 +00:00
const Point2 icon_offset = Point2 ( 0 , Math : : floor ( ( h - icon_size . height ) / 2.0 ) ) ;
Point2 icon_pos ;
2022-02-06 18:19:04 +00:00
if ( items [ i ] . separator ) {
2022-09-01 10:38:08 +00:00
separator_ofs - = ( icon_size . width + theme_cache . h_separation ) / 2 ;
2022-02-06 18:19:04 +00:00
if ( rtl ) {
2023-03-31 19:17:59 +00:00
icon_pos = Size2 ( control - > get_size ( ) . width - item_ofs . x - separator_ofs - icon_size . width , item_ofs . y ) ;
2022-02-06 18:19:04 +00:00
} else {
2023-03-31 19:17:59 +00:00
icon_pos = item_ofs + Size2 ( separator_ofs , 0 ) ;
2023-10-17 18:38:35 +00:00
separator_ofs + = icon_size . width + theme_cache . h_separation ;
2022-02-06 18:19:04 +00:00
}
2020-09-03 11:22:16 +00:00
} else {
2022-02-06 18:19:04 +00:00
if ( rtl ) {
2023-03-31 19:17:59 +00:00
icon_pos = Size2 ( control - > get_size ( ) . width - item_ofs . x - check_ofs - icon_size . width , item_ofs . y ) ;
2022-02-06 18:19:04 +00:00
} else {
2023-03-31 19:17:59 +00:00
icon_pos = item_ofs + Size2 ( check_ofs , 0 ) ;
2022-02-06 18:19:04 +00:00
}
2020-09-03 11:22:16 +00:00
}
2023-03-31 19:17:59 +00:00
items [ i ] . icon - > draw_rect ( ci , Rect2 ( icon_pos + icon_offset , icon_size ) , false , icon_color ) ;
2020-03-12 12:37:40 +00:00
}
2016-03-08 23:00:52 +00:00
2022-02-06 18:19:04 +00:00
// Submenu arrow on right hand side.
2023-11-28 16:33:04 +00:00
if ( items [ i ] . submenu ) {
2020-09-03 11:22:16 +00:00
if ( rtl ) {
2024-02-23 13:51:47 +00:00
submenu - > draw ( ci , Point2 ( theme_cache . panel_style - > get_margin ( SIDE_LEFT ) + theme_cache . item_end_padding , item_ofs . y + Math : : floor ( h - submenu - > get_height ( ) ) / 2 ) , icon_color ) ;
2020-09-03 11:22:16 +00:00
} else {
2022-09-01 10:38:08 +00:00
submenu - > draw ( ci , Point2 ( display_width - theme_cache . panel_style - > get_margin ( SIDE_RIGHT ) - submenu - > get_width ( ) - theme_cache . item_end_padding , item_ofs . y + Math : : floor ( h - submenu - > get_height ( ) ) / 2 ) , icon_color ) ;
2020-09-03 11:22:16 +00:00
}
2020-03-12 12:37:40 +00:00
}
2019-03-18 13:10:19 +00:00
2022-04-14 02:46:35 +00:00
// Text
2020-03-12 12:37:40 +00:00
if ( items [ i ] . separator ) {
2021-12-09 09:42:46 +00:00
if ( ! text . is_empty ( ) ) {
2022-02-06 18:19:04 +00:00
Vector2 text_pos = Point2 ( separator_ofs , item_ofs . y + Math : : floor ( ( h - items [ i ] . text_buf - > get_size ( ) . y ) / 2.0 ) ) ;
2022-09-01 10:38:08 +00:00
if ( theme_cache . font_separator_outline_size > 0 & & theme_cache . font_separator_outline_color . a > 0 ) {
items [ i ] . text_buf - > draw_outline ( ci , text_pos , theme_cache . font_separator_outline_size , theme_cache . font_separator_outline_color ) ;
2020-12-25 21:45:28 +00:00
}
2022-09-01 10:38:08 +00:00
items [ i ] . text_buf - > draw ( ci , text_pos , theme_cache . font_separator_color ) ;
2020-03-12 12:37:40 +00:00
}
} else {
item_ofs . x + = icon_ofs + check_ofs ;
2022-04-14 02:46:35 +00:00
2020-09-03 11:22:16 +00:00
if ( rtl ) {
2020-12-25 21:45:28 +00:00
Vector2 text_pos = Size2 ( control - > get_size ( ) . width - items [ i ] . text_buf - > get_size ( ) . width - item_ofs . x , item_ofs . y ) + Point2 ( 0 , Math : : floor ( ( h - items [ i ] . text_buf - > get_size ( ) . y ) / 2.0 ) ) ;
2022-09-01 10:38:08 +00:00
if ( theme_cache . font_outline_size > 0 & & theme_cache . font_outline_color . a > 0 ) {
items [ i ] . text_buf - > draw_outline ( ci , text_pos , theme_cache . font_outline_size , theme_cache . font_outline_color ) ;
2020-12-25 21:45:28 +00:00
}
2022-09-01 10:38:08 +00:00
items [ i ] . text_buf - > draw ( ci , text_pos , items [ i ] . disabled ? theme_cache . font_disabled_color : ( i = = mouse_over ? theme_cache . font_hover_color : theme_cache . font_color ) ) ;
2020-09-03 11:22:16 +00:00
} else {
2020-12-25 21:45:28 +00:00
Vector2 text_pos = item_ofs + Point2 ( 0 , Math : : floor ( ( h - items [ i ] . text_buf - > get_size ( ) . y ) / 2.0 ) ) ;
2022-09-01 10:38:08 +00:00
if ( theme_cache . font_outline_size > 0 & & theme_cache . font_outline_color . a > 0 ) {
items [ i ] . text_buf - > draw_outline ( ci , text_pos , theme_cache . font_outline_size , theme_cache . font_outline_color ) ;
2020-12-25 21:45:28 +00:00
}
2022-09-01 10:38:08 +00:00
items [ i ] . text_buf - > draw ( ci , text_pos , items [ i ] . disabled ? theme_cache . font_disabled_color : ( i = = mouse_over ? theme_cache . font_hover_color : theme_cache . font_color ) ) ;
2020-09-03 11:22:16 +00:00
}
2020-03-12 12:37:40 +00:00
}
2014-02-10 01:10:30 +00:00
2020-08-29 12:05:59 +00:00
// Accelerator / Shortcut
2021-08-13 21:31:57 +00:00
if ( items [ i ] . accel ! = Key : : NONE | | ( items [ i ] . shortcut . is_valid ( ) & & items [ i ] . shortcut - > has_valid_event ( ) ) ) {
2020-09-03 11:22:16 +00:00
if ( rtl ) {
2024-02-23 13:51:47 +00:00
item_ofs . x = theme_cache . panel_style - > get_margin ( SIDE_LEFT ) + theme_cache . item_end_padding ;
2020-09-03 11:22:16 +00:00
} else {
2022-09-01 10:38:08 +00:00
item_ofs . x = display_width - theme_cache . panel_style - > get_margin ( SIDE_RIGHT ) - items [ i ] . accel_text_buf - > get_size ( ) . x - theme_cache . item_end_padding ;
2020-09-03 11:22:16 +00:00
}
2020-12-25 21:45:28 +00:00
Vector2 text_pos = item_ofs + Point2 ( 0 , Math : : floor ( ( h - items [ i ] . text_buf - > get_size ( ) . y ) / 2.0 ) ) ;
2022-09-01 10:38:08 +00:00
if ( theme_cache . font_outline_size > 0 & & theme_cache . font_outline_color . a > 0 ) {
items [ i ] . accel_text_buf - > draw_outline ( ci , text_pos , theme_cache . font_outline_size , theme_cache . font_outline_color ) ;
2020-12-25 21:45:28 +00:00
}
2022-09-01 10:38:08 +00:00
items [ i ] . accel_text_buf - > draw ( ci , text_pos , i = = mouse_over ? theme_cache . font_hover_color : theme_cache . font_accelerator_color ) ;
2020-03-12 12:37:40 +00:00
}
2018-07-14 21:15:42 +00:00
2022-02-06 18:19:04 +00:00
// Cache the item vertical offset from the first item and the height.
2020-03-12 12:37:40 +00:00
items . write [ i ] . _ofs_cache = ofs . y ;
2020-08-29 12:05:59 +00:00
items . write [ i ] . _height_cache = h ;
2014-02-10 01:10:30 +00:00
2020-03-12 12:37:40 +00:00
ofs . y + = h ;
}
}
2014-02-10 01:10:30 +00:00
2020-09-07 12:57:50 +00:00
void PopupMenu : : _minimum_lifetime_timeout ( ) {
close_allowed = true ;
// If the mouse still isn't in this popup after timer expires, close.
2022-04-13 06:58:38 +00:00
if ( ! activated_by_keyboard & & ! get_visible_rect ( ) . has_point ( get_mouse_position ( ) ) ) {
2020-09-07 12:57:50 +00:00
_close_pressed ( ) ;
}
}
void PopupMenu : : _close_pressed ( ) {
// Only apply minimum lifetime to submenus.
PopupMenu * parent_pum = Object : : cast_to < PopupMenu > ( get_parent ( ) ) ;
if ( ! parent_pum ) {
Popup : : _close_pressed ( ) ;
return ;
}
// If the timer has expired, close. If timer is still running, do nothing.
if ( close_allowed ) {
close_allowed = false ;
Popup : : _close_pressed ( ) ;
} else if ( minimum_lifetime_timer - > is_stopped ( ) ) {
minimum_lifetime_timer - > start ( ) ;
}
}
2023-05-24 13:58:50 +00:00
void PopupMenu : : _shape_item ( int p_idx ) {
if ( items . write [ p_idx ] . dirty ) {
items . write [ p_idx ] . text_buf - > clear ( ) ;
2020-09-03 11:22:16 +00:00
2023-05-24 13:58:50 +00:00
Ref < Font > font = items [ p_idx ] . separator ? theme_cache . font_separator : theme_cache . font ;
int font_size = items [ p_idx ] . separator ? theme_cache . font_separator_size : theme_cache . font_size ;
2020-09-03 11:22:16 +00:00
2023-05-24 13:58:50 +00:00
if ( items [ p_idx ] . text_direction = = Control : : TEXT_DIRECTION_INHERITED ) {
items . write [ p_idx ] . text_buf - > set_direction ( is_layout_rtl ( ) ? TextServer : : DIRECTION_RTL : TextServer : : DIRECTION_LTR ) ;
2020-09-03 11:22:16 +00:00
} else {
2023-05-24 13:58:50 +00:00
items . write [ p_idx ] . text_buf - > set_direction ( ( TextServer : : Direction ) items [ p_idx ] . text_direction ) ;
2020-09-03 11:22:16 +00:00
}
2023-05-24 13:58:50 +00:00
items . write [ p_idx ] . text_buf - > add_string ( items . write [ p_idx ] . xl_text , font , font_size , items [ p_idx ] . language ) ;
2020-09-03 11:22:16 +00:00
2023-05-24 13:58:50 +00:00
items . write [ p_idx ] . accel_text_buf - > clear ( ) ;
items . write [ p_idx ] . accel_text_buf - > set_direction ( is_layout_rtl ( ) ? TextServer : : DIRECTION_RTL : TextServer : : DIRECTION_LTR ) ;
items . write [ p_idx ] . accel_text_buf - > add_string ( _get_accel_text ( items . write [ p_idx ] ) , font , font_size ) ;
items . write [ p_idx ] . dirty = false ;
2020-09-03 11:22:16 +00:00
}
}
2022-08-01 09:28:16 +00:00
void PopupMenu : : _menu_changed ( ) {
emit_signal ( SNAME ( " menu_changed " ) ) ;
}
void PopupMenu : : add_child_notify ( Node * p_child ) {
Window : : add_child_notify ( p_child ) ;
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
PopupMenu * pm = Object : : cast_to < PopupMenu > ( p_child ) ;
2023-07-11 08:17:35 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2023-11-28 16:33:04 +00:00
if ( items [ i ] . submenu = = p_child ) {
2024-01-19 17:41:01 +00:00
RID submenu_rid = pm - > bind_global_menu ( ) ;
NativeMenu : : get_singleton ( ) - > set_item_submenu ( global_menu , i , submenu_rid ) ;
2023-07-11 08:17:35 +00:00
items . write [ i ] . submenu_bound = true ;
}
}
2022-08-01 09:28:16 +00:00
}
_menu_changed ( ) ;
}
void PopupMenu : : remove_child_notify ( Node * p_child ) {
Window : : remove_child_notify ( p_child ) ;
PopupMenu * pm = Object : : cast_to < PopupMenu > ( p_child ) ;
if ( ! pm ) {
return ;
}
2024-01-19 17:41:01 +00:00
if ( Object : : cast_to < PopupMenu > ( p_child ) & & global_menu . is_valid ( ) ) {
2023-07-11 08:17:35 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2023-11-28 16:33:04 +00:00
if ( items [ i ] . submenu = = p_child ) {
2024-01-19 17:41:01 +00:00
NativeMenu : : get_singleton ( ) - > set_item_submenu ( global_menu , i , RID ( ) ) ;
2023-07-11 08:17:35 +00:00
items . write [ i ] . submenu_bound = false ;
}
}
pm - > unbind_global_menu ( ) ;
}
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
}
2020-03-12 12:37:40 +00:00
void PopupMenu : : _notification ( int p_what ) {
switch ( p_what ) {
case NOTIFICATION_ENTER_TREE : {
PopupMenu * pm = Object : : cast_to < PopupMenu > ( get_parent ( ) ) ;
if ( pm ) {
2022-02-07 02:07:08 +00:00
// Inherit submenu's popup delay time from parent menu.
2020-03-12 12:37:40 +00:00
float pm_delay = pm - > get_submenu_popup_delay ( ) ;
set_submenu_popup_delay ( pm_delay ) ;
2014-02-10 01:10:30 +00:00
}
2023-02-01 08:51:03 +00:00
if ( ! is_embedded ( ) ) {
set_flag ( FLAG_NO_FOCUS , true ) ;
}
2024-01-19 17:41:01 +00:00
if ( system_menu_id ! = NativeMenu : : INVALID_MENU_ID ) {
2023-10-15 17:52:56 +00:00
bind_global_menu ( ) ;
}
} break ;
case NOTIFICATION_EXIT_TREE : {
2024-01-19 17:41:01 +00:00
if ( system_menu_id ! = NativeMenu : : INVALID_MENU_ID ) {
2023-10-15 17:52:56 +00:00
unbind_global_menu ( ) ;
}
2014-02-10 01:10:30 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2024-02-23 13:51:47 +00:00
case NOTIFICATION_THEME_CHANGED : {
scroll_container - > add_theme_style_override ( " panel " , theme_cache . panel_style ) ;
[[fallthrough]] ;
}
2022-02-09 04:36:20 +00:00
case Control : : NOTIFICATION_LAYOUT_DIRECTION_CHANGED :
2020-03-12 12:37:40 +00:00
case NOTIFICATION_TRANSLATION_CHANGED : {
2024-01-19 17:41:01 +00:00
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
bool is_global = global_menu . is_valid ( ) ;
2024-03-07 18:42:24 +00:00
if ( is_global ) {
nmenu - > set_interface_direction ( global_menu , control - > is_layout_rtl ( ) ) ;
}
2020-03-12 12:37:40 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2023-07-11 08:17:35 +00:00
Item & item = items . write [ i ] ;
item . xl_text = atr ( item . text ) ;
item . dirty = true ;
if ( is_global ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_text ( global_menu , i , item . xl_text ) ;
2023-07-11 08:17:35 +00:00
}
2021-03-20 03:37:09 +00:00
_shape_item ( i ) ;
2020-03-12 12:37:40 +00:00
}
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2018-01-02 07:10:49 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2020-03-12 12:37:40 +00:00
case NOTIFICATION_WM_MOUSE_ENTER : {
2020-09-07 12:57:50 +00:00
grab_focus ( ) ;
2014-02-10 01:10:30 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2020-03-12 12:37:40 +00:00
case NOTIFICATION_WM_MOUSE_EXIT : {
2023-11-28 16:33:04 +00:00
if ( mouse_over > = 0 & & ( ! items [ mouse_over ] . submenu | | submenu_over ! = - 1 ) ) {
2017-12-15 17:17:16 +00:00
mouse_over = - 1 ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2017-12-15 17:17:16 +00:00
}
} break ;
2022-02-15 17:06:48 +00:00
2024-02-20 09:05:41 +00:00
case NOTIFICATION_POST_POPUP : {
initial_button_mask = Input : : get_singleton ( ) - > get_mouse_button_mask ( ) ;
during_grabbed_click = ( bool ) initial_button_mask ;
} break ;
2020-03-20 02:32:09 +00:00
case NOTIFICATION_INTERNAL_PROCESS : {
2023-02-09 16:25:56 +00:00
Input * input = Input : : get_singleton ( ) ;
if ( input - > is_action_just_released ( " ui_up " ) | | input - > is_action_just_released ( " ui_down " ) ) {
gamepad_event_delay_ms = DEFAULT_GAMEPAD_EVENT_DELAY_MS ;
set_process_internal ( false ) ;
return ;
}
gamepad_event_delay_ms - = get_process_delta_time ( ) ;
if ( gamepad_event_delay_ms < = 0 ) {
if ( input - > is_action_pressed ( " ui_down " ) ) {
gamepad_event_delay_ms = GAMEPAD_EVENT_REPEAT_RATE_MS + gamepad_event_delay_ms ;
int search_from = mouse_over + 1 ;
if ( search_from > = items . size ( ) ) {
search_from = 0 ;
}
bool match_found = false ;
for ( int i = search_from ; i < items . size ( ) ; i + + ) {
if ( ! items [ i ] . separator & & ! items [ i ] . disabled ) {
mouse_over = i ;
emit_signal ( SNAME ( " id_focused " ) , i ) ;
scroll_to_item ( i ) ;
control - > queue_redraw ( ) ;
match_found = true ;
break ;
}
}
if ( ! match_found ) {
// If the last item is not selectable, try re-searching from the start.
for ( int i = 0 ; i < search_from ; i + + ) {
if ( ! items [ i ] . separator & & ! items [ i ] . disabled ) {
mouse_over = i ;
emit_signal ( SNAME ( " id_focused " ) , i ) ;
scroll_to_item ( i ) ;
control - > queue_redraw ( ) ;
break ;
}
}
}
}
if ( input - > is_action_pressed ( " ui_up " ) ) {
gamepad_event_delay_ms = GAMEPAD_EVENT_REPEAT_RATE_MS + gamepad_event_delay_ms ;
int search_from = mouse_over - 1 ;
if ( search_from < 0 ) {
search_from = items . size ( ) - 1 ;
}
bool match_found = false ;
for ( int i = search_from ; i > = 0 ; i - - ) {
if ( ! items [ i ] . separator & & ! items [ i ] . disabled ) {
mouse_over = i ;
emit_signal ( SNAME ( " id_focused " ) , i ) ;
scroll_to_item ( i ) ;
control - > queue_redraw ( ) ;
match_found = true ;
break ;
}
}
if ( ! match_found ) {
// If the first item is not selectable, try re-searching from the end.
for ( int i = items . size ( ) - 1 ; i > = search_from ; i - - ) {
if ( ! items [ i ] . separator & & ! items [ i ] . disabled ) {
mouse_over = i ;
emit_signal ( SNAME ( " id_focused " ) , i ) ;
scroll_to_item ( i ) ;
control - > queue_redraw ( ) ;
break ;
}
}
}
}
}
2022-02-15 17:06:48 +00:00
// Only used when using operating system windows.
2022-04-13 06:58:38 +00:00
if ( ! activated_by_keyboard & & ! is_embedded ( ) & & autohide_areas . size ( ) ) {
2020-03-20 02:32:09 +00:00
Point2 mouse_pos = DisplayServer : : get_singleton ( ) - > mouse_get_position ( ) ;
mouse_pos - = get_position ( ) ;
2021-07-24 13:46:25 +00:00
for ( const Rect2 & E : autohide_areas ) {
2021-07-16 03:45:57 +00:00
if ( ! Rect2 ( Point2 ( ) , get_size ( ) ) . has_point ( mouse_pos ) & & E . has_point ( mouse_pos ) ) {
2020-03-20 02:32:09 +00:00
_close_pressed ( ) ;
return ;
}
}
}
2018-03-27 21:41:27 +00:00
} break ;
2022-02-15 17:06:48 +00:00
2020-03-12 12:37:40 +00:00
case NOTIFICATION_VISIBILITY_CHANGED : {
if ( ! is_visible ( ) ) {
if ( mouse_over > = 0 ) {
mouse_over = - 1 ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
}
2018-09-11 04:44:19 +00:00
2020-03-12 12:37:40 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2023-11-28 16:33:04 +00:00
if ( ! items [ i ] . submenu ) {
2020-03-12 12:37:40 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2023-11-28 16:33:04 +00:00
items [ i ] . submenu - > hide ( ) ;
2020-03-12 12:37:40 +00:00
}
2020-03-20 02:32:09 +00:00
set_process_internal ( false ) ;
} else {
2021-07-23 15:13:01 +00:00
if ( ! is_embedded ( ) ) {
2020-03-20 02:32:09 +00:00
set_process_internal ( true ) ;
}
2018-09-11 04:44:19 +00:00
}
2014-02-10 01:10:30 +00:00
} break ;
}
}
2019-10-08 06:28:26 +00:00
/* Methods to add items with or without icon, checkbox, shortcut.
* Be sure to keep them in sync when adding new properties in the Item struct .
*/
2014-02-10 01:10:30 +00:00
2019-10-08 07:33:22 +00:00
# define ITEM_SETUP_WITH_ACCEL(p_label, p_id, p_accel) \
item . text = p_label ; \
2021-05-27 17:31:33 +00:00
item . xl_text = atr ( p_label ) ; \
2022-02-08 23:53:57 +00:00
item . id = p_id = = - 1 ? items . size ( ) : p_id ; \
2019-10-08 07:33:22 +00:00
item . accel = p_accel ;
2021-08-13 21:31:57 +00:00
void PopupMenu : : add_item ( const String & p_label , int p_id , Key p_accel ) {
2014-02-10 01:10:30 +00:00
Item item ;
2019-10-08 07:33:22 +00:00
ITEM_SETUP_WITH_ACCEL ( p_label , p_id , p_accel ) ;
2014-02-10 01:10:30 +00:00
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( item . accel ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , index , item . accel ) ;
2023-07-11 08:17:35 +00:00
}
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2021-11-03 02:08:58 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-08-13 21:31:57 +00:00
void PopupMenu : : add_icon_item ( const Ref < Texture2D > & p_icon , const String & p_label , int p_id , Key p_accel ) {
2014-02-10 01:10:30 +00:00
Item item ;
2019-10-08 07:33:22 +00:00
ITEM_SETUP_WITH_ACCEL ( p_label , p_id , p_accel ) ;
2019-10-08 06:28:26 +00:00
item . icon = p_icon ;
2014-02-10 01:10:30 +00:00
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( item . accel ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , index , item . accel ) ;
2023-07-11 08:17:35 +00:00
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_icon ( global_menu , index , item . icon ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2021-11-03 02:08:58 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-08-13 21:31:57 +00:00
void PopupMenu : : add_check_item ( const String & p_label , int p_id , Key p_accel ) {
2014-02-10 01:10:30 +00:00
Item item ;
2019-10-08 07:33:22 +00:00
ITEM_SETUP_WITH_ACCEL ( p_label , p_id , p_accel ) ;
2018-03-23 20:55:40 +00:00
item . checkable_type = Item : : CHECKABLE_TYPE_CHECK_BOX ;
2014-02-10 01:10:30 +00:00
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( item . accel ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , index , item . accel ) ;
2023-07-11 08:17:35 +00:00
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_checkable ( global_menu , index , true ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2018-03-23 20:55:40 +00:00
2021-08-13 21:31:57 +00:00
void PopupMenu : : add_icon_check_item ( const Ref < Texture2D > & p_icon , const String & p_label , int p_id , Key p_accel ) {
2014-02-10 01:10:30 +00:00
Item item ;
2019-10-08 07:33:22 +00:00
ITEM_SETUP_WITH_ACCEL ( p_label , p_id , p_accel ) ;
2019-10-08 06:28:26 +00:00
item . icon = p_icon ;
2018-03-23 20:55:40 +00:00
item . checkable_type = Item : : CHECKABLE_TYPE_CHECK_BOX ;
2014-02-10 01:10:30 +00:00
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( item . accel ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , index , item . accel ) ;
2023-07-11 08:17:35 +00:00
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_icon ( global_menu , index , item . icon ) ;
nmenu - > set_item_checkable ( global_menu , index , true ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-08-13 21:31:57 +00:00
void PopupMenu : : add_radio_check_item ( const String & p_label , int p_id , Key p_accel ) {
2019-10-08 07:33:22 +00:00
Item item ;
ITEM_SETUP_WITH_ACCEL ( p_label , p_id , p_accel ) ;
item . checkable_type = Item : : CHECKABLE_TYPE_RADIO_BUTTON ;
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( item . accel ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , index , item . accel ) ;
2023-07-11 08:17:35 +00:00
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_radio_checkable ( global_menu , index , true ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2018-03-23 20:55:40 +00:00
}
2021-08-13 21:31:57 +00:00
void PopupMenu : : add_icon_radio_check_item ( const Ref < Texture2D > & p_icon , const String & p_label , int p_id , Key p_accel ) {
2019-10-08 07:33:22 +00:00
Item item ;
ITEM_SETUP_WITH_ACCEL ( p_label , p_id , p_accel ) ;
item . icon = p_icon ;
item . checkable_type = Item : : CHECKABLE_TYPE_RADIO_BUTTON ;
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( item . accel ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , index , item . accel ) ;
2023-07-11 08:17:35 +00:00
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_icon ( global_menu , index , item . icon ) ;
nmenu - > set_item_radio_checkable ( global_menu , index , true ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2018-04-09 17:55:24 +00:00
}
2021-08-13 21:31:57 +00:00
void PopupMenu : : add_multistate_item ( const String & p_label , int p_max_states , int p_default_state , int p_id , Key p_accel ) {
2019-10-08 06:28:26 +00:00
Item item ;
2019-10-08 07:33:22 +00:00
ITEM_SETUP_WITH_ACCEL ( p_label , p_id , p_accel ) ;
2019-10-08 06:28:26 +00:00
item . max_states = p_max_states ;
item . state = p_default_state ;
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( item . accel ! = Key : : NONE ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_accelerator ( global_menu , index , item . accel ) ;
2023-07-11 08:17:35 +00:00
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_max_states ( global_menu , index , item . max_states ) ;
nmenu - > set_item_state ( global_menu , index , item . state ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2019-10-08 06:28:26 +00:00
}
2020-02-23 21:23:34 +00:00
# define ITEM_SETUP_WITH_SHORTCUT(p_shortcut, p_id, p_global, p_allow_echo) \
2020-09-09 19:53:24 +00:00
ERR_FAIL_COND_MSG ( p_shortcut . is_null ( ) , " Cannot add item with invalid Shortcut. " ) ; \
2019-10-08 07:33:22 +00:00
_ref_shortcut ( p_shortcut ) ; \
item . text = p_shortcut - > get_name ( ) ; \
2021-05-27 17:31:33 +00:00
item . xl_text = atr ( item . text ) ; \
2022-02-08 23:53:57 +00:00
item . id = p_id = = - 1 ? items . size ( ) : p_id ; \
2019-10-08 07:33:22 +00:00
item . shortcut = p_shortcut ; \
2020-02-23 21:23:34 +00:00
item . shortcut_is_global = p_global ; \
item . allow_echo = p_allow_echo ;
2016-06-05 00:31:29 +00:00
2020-02-23 21:23:34 +00:00
void PopupMenu : : add_shortcut ( const Ref < Shortcut > & p_shortcut , int p_id , bool p_global , bool p_allow_echo ) {
2016-06-05 00:31:29 +00:00
Item item ;
2020-02-23 21:23:34 +00:00
ITEM_SETUP_WITH_SHORTCUT ( p_shortcut , p_id , p_global , p_allow_echo ) ;
2016-06-05 00:31:29 +00:00
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , p_global ? callable_mp ( this , & PopupMenu : : activate_item ) : Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( ! item . shortcut_is_disabled & & item . shortcut . is_valid ( ) & & item . shortcut - > has_valid_event ( ) ) {
Array events = item . shortcut - > get_events ( ) ;
for ( int j = 0 ; j < events . size ( ) ; j + + ) {
Ref < InputEventKey > ie = events [ j ] ;
2023-10-06 06:36:42 +00:00
if ( ie . is_valid ( ) & & _set_item_accelerator ( index , ie ) ) {
2023-07-11 08:17:35 +00:00
break ;
}
}
}
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2016-06-05 00:31:29 +00:00
}
2020-02-23 21:23:34 +00:00
void PopupMenu : : add_icon_shortcut ( const Ref < Texture2D > & p_icon , const Ref < Shortcut > & p_shortcut , int p_id , bool p_global , bool p_allow_echo ) {
2016-06-05 00:31:29 +00:00
Item item ;
2020-02-23 21:23:34 +00:00
ITEM_SETUP_WITH_SHORTCUT ( p_shortcut , p_id , p_global , p_allow_echo ) ;
2019-10-08 06:28:26 +00:00
item . icon = p_icon ;
2016-06-05 00:31:29 +00:00
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , p_global ? callable_mp ( this , & PopupMenu : : activate_item ) : Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( ! item . shortcut_is_disabled & & item . shortcut . is_valid ( ) & & item . shortcut - > has_valid_event ( ) ) {
Array events = item . shortcut - > get_events ( ) ;
for ( int j = 0 ; j < events . size ( ) ; j + + ) {
Ref < InputEventKey > ie = events [ j ] ;
2023-10-06 06:36:42 +00:00
if ( ie . is_valid ( ) & & _set_item_accelerator ( index , ie ) ) {
2023-07-11 08:17:35 +00:00
break ;
}
}
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_icon ( global_menu , index , item . icon ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2016-06-05 00:31:29 +00:00
}
2018-03-23 20:55:40 +00:00
2020-09-09 19:53:24 +00:00
void PopupMenu : : add_check_shortcut ( const Ref < Shortcut > & p_shortcut , int p_id , bool p_global ) {
2016-06-05 00:31:29 +00:00
Item item ;
2020-02-23 21:23:34 +00:00
ITEM_SETUP_WITH_SHORTCUT ( p_shortcut , p_id , p_global , false ) ; // Echo for check shortcuts doesn't make sense.
2019-10-08 06:28:26 +00:00
item . checkable_type = Item : : CHECKABLE_TYPE_CHECK_BOX ;
2016-06-05 00:31:29 +00:00
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , p_global ? callable_mp ( this , & PopupMenu : : activate_item ) : Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( ! item . shortcut_is_disabled & & item . shortcut . is_valid ( ) & & item . shortcut - > has_valid_event ( ) ) {
Array events = item . shortcut - > get_events ( ) ;
for ( int j = 0 ; j < events . size ( ) ; j + + ) {
Ref < InputEventKey > ie = events [ j ] ;
2023-10-06 06:36:42 +00:00
if ( ie . is_valid ( ) & & _set_item_accelerator ( index , ie ) ) {
2023-07-11 08:17:35 +00:00
break ;
}
}
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_checkable ( global_menu , index , true ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2016-06-05 00:31:29 +00:00
}
2020-09-09 19:53:24 +00:00
void PopupMenu : : add_icon_check_shortcut ( const Ref < Texture2D > & p_icon , const Ref < Shortcut > & p_shortcut , int p_id , bool p_global ) {
2016-06-05 00:31:29 +00:00
Item item ;
2020-02-23 21:23:34 +00:00
ITEM_SETUP_WITH_SHORTCUT ( p_shortcut , p_id , p_global , false ) ;
2019-10-08 06:28:26 +00:00
item . icon = p_icon ;
2019-10-08 07:33:22 +00:00
item . checkable_type = Item : : CHECKABLE_TYPE_CHECK_BOX ;
2016-06-05 00:31:29 +00:00
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , p_global ? callable_mp ( this , & PopupMenu : : activate_item ) : Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( ! item . shortcut_is_disabled & & item . shortcut . is_valid ( ) & & item . shortcut - > has_valid_event ( ) ) {
Array events = item . shortcut - > get_events ( ) ;
for ( int j = 0 ; j < events . size ( ) ; j + + ) {
Ref < InputEventKey > ie = events [ j ] ;
2023-10-06 06:36:42 +00:00
if ( ie . is_valid ( ) & & _set_item_accelerator ( index , ie ) ) {
2023-07-11 08:17:35 +00:00
break ;
}
}
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_icon ( global_menu , index , item . icon ) ;
nmenu - > set_item_checkable ( global_menu , index , true ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2016-06-05 00:31:29 +00:00
}
2020-09-09 19:53:24 +00:00
void PopupMenu : : add_radio_check_shortcut ( const Ref < Shortcut > & p_shortcut , int p_id , bool p_global ) {
2019-10-08 07:33:22 +00:00
Item item ;
2020-02-23 21:23:34 +00:00
ITEM_SETUP_WITH_SHORTCUT ( p_shortcut , p_id , p_global , false ) ;
2019-10-08 07:33:22 +00:00
item . checkable_type = Item : : CHECKABLE_TYPE_RADIO_BUTTON ;
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , p_global ? callable_mp ( this , & PopupMenu : : activate_item ) : Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( ! item . shortcut_is_disabled & & item . shortcut . is_valid ( ) & & item . shortcut - > has_valid_event ( ) ) {
Array events = item . shortcut - > get_events ( ) ;
for ( int j = 0 ; j < events . size ( ) ; j + + ) {
Ref < InputEventKey > ie = events [ j ] ;
2023-10-06 06:36:42 +00:00
if ( ie . is_valid ( ) & & _set_item_accelerator ( index , ie ) ) {
2023-07-11 08:17:35 +00:00
break ;
}
}
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_radio_checkable ( global_menu , index , true ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2018-03-23 20:55:40 +00:00
}
2020-09-09 19:53:24 +00:00
void PopupMenu : : add_icon_radio_check_shortcut ( const Ref < Texture2D > & p_icon , const Ref < Shortcut > & p_shortcut , int p_id , bool p_global ) {
2019-10-08 07:33:22 +00:00
Item item ;
2020-02-23 21:23:34 +00:00
ITEM_SETUP_WITH_SHORTCUT ( p_shortcut , p_id , p_global , false ) ;
2019-10-08 07:33:22 +00:00
item . icon = p_icon ;
item . checkable_type = Item : : CHECKABLE_TYPE_RADIO_BUTTON ;
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , p_global ? callable_mp ( this , & PopupMenu : : activate_item ) : Callable ( ) , items . size ( ) - 1 ) ;
2023-07-11 08:17:35 +00:00
if ( ! item . shortcut_is_disabled & & item . shortcut . is_valid ( ) & & item . shortcut - > has_valid_event ( ) ) {
Array events = item . shortcut - > get_events ( ) ;
for ( int j = 0 ; j < events . size ( ) ; j + + ) {
Ref < InputEventKey > ie = events [ j ] ;
2023-10-06 06:36:42 +00:00
if ( ie . is_valid ( ) & & _set_item_accelerator ( index , ie ) ) {
2023-07-11 08:17:35 +00:00
break ;
}
}
}
2024-01-19 17:41:01 +00:00
nmenu - > set_item_icon ( global_menu , index , item . icon ) ;
nmenu - > set_item_radio_checkable ( global_menu , index , true ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2019-10-08 06:28:26 +00:00
}
void PopupMenu : : add_submenu_item ( const String & p_label , const String & p_submenu , int p_id ) {
2023-11-28 16:33:04 +00:00
PopupMenu * pm = Object : : cast_to < PopupMenu > ( get_node_or_null ( p_submenu ) ) ;
ERR_FAIL_NULL_MSG ( pm , vformat ( " Child PopupMenu \" %s \" does not exist. " , p_submenu ) ) ;
add_submenu_node_item ( p_label , pm , p_id ) ;
}
void PopupMenu : : add_submenu_node_item ( const String & p_label , PopupMenu * p_submenu , int p_id ) {
ERR_FAIL_NULL ( p_submenu ) ;
if ( p_submenu - > get_parent ( ) ! = this ) {
ERR_FAIL_COND_MSG ( p_submenu - > get_parent ( ) ! = nullptr , vformat ( " The submenu \" %s \" already has a different parent. " , p_submenu - > get_name ( ) ) ) ;
add_child ( p_submenu ) ;
2023-11-09 14:37:08 +00:00
}
2017-11-22 21:29:27 +00:00
Item item ;
item . text = p_label ;
2021-05-27 17:31:33 +00:00
item . xl_text = atr ( p_label ) ;
2022-02-08 23:53:57 +00:00
item . id = p_id = = - 1 ? items . size ( ) : p_id ;
2019-10-08 06:28:26 +00:00
item . submenu = p_submenu ;
2023-11-28 16:33:04 +00:00
item . submenu_name = p_submenu - > get_name ( ) ;
2017-11-22 21:29:27 +00:00
items . push_back ( item ) ;
2023-03-31 19:17:59 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
int index = nmenu - > add_item ( global_menu , item . xl_text , callable_mp ( this , & PopupMenu : : activate_item ) , Callable ( ) , items . size ( ) - 1 ) ;
RID submenu_rid = p_submenu - > bind_global_menu ( ) ;
nmenu - > set_item_submenu ( global_menu , index , submenu_rid ) ;
2023-11-28 16:33:04 +00:00
items . write [ index ] . submenu_bound = true ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( items . size ( ) - 1 ) ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2023-03-31 19:17:59 +00:00
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2023-07-11 08:17:35 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2017-11-22 21:29:27 +00:00
}
2019-10-08 07:33:22 +00:00
# undef ITEM_SETUP_WITH_ACCEL
# undef ITEM_SETUP_WITH_SHORTCUT
2019-10-08 06:28:26 +00:00
/* Methods to modify existing items. */
2014-02-10 01:10:30 +00:00
void PopupMenu : : set_item_text ( int p_idx , const String & p_text ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-15 12:07:50 +00:00
if ( items [ p_idx ] . text = = p_text ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . text = p_text ;
2021-05-27 17:31:33 +00:00
items . write [ p_idx ] . xl_text = atr ( p_text ) ;
2022-03-15 12:07:50 +00:00
items . write [ p_idx ] . dirty = true ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_text ( global_menu , p_idx , items [ p_idx ] . xl_text ) ;
2023-07-11 08:17:35 +00:00
}
2020-09-03 11:22:16 +00:00
_shape_item ( p_idx ) ;
2016-03-08 23:00:52 +00:00
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2023-05-24 13:58:50 +00:00
void PopupMenu : : set_item_text_direction ( int p_idx , Control : : TextDirection p_text_direction ) {
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
2022-03-12 00:06:45 +00:00
}
2023-05-24 13:58:50 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2020-09-03 11:22:16 +00:00
ERR_FAIL_COND ( ( int ) p_text_direction < - 1 | | ( int ) p_text_direction > 3 ) ;
2023-05-24 13:58:50 +00:00
if ( items [ p_idx ] . text_direction ! = p_text_direction ) {
items . write [ p_idx ] . text_direction = p_text_direction ;
items . write [ p_idx ] . dirty = true ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-09-03 11:22:16 +00:00
}
}
2023-05-24 13:58:50 +00:00
void PopupMenu : : set_item_language ( int p_idx , const String & p_language ) {
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
2022-03-12 00:06:45 +00:00
}
2023-05-24 13:58:50 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
if ( items [ p_idx ] . language ! = p_language ) {
items . write [ p_idx ] . language = p_language ;
items . write [ p_idx ] . dirty = true ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-09-03 11:22:16 +00:00
}
}
2019-06-11 18:43:37 +00:00
void PopupMenu : : set_item_icon ( int p_idx , const Ref < Texture2D > & p_icon ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . icon = = p_icon ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . icon = p_icon ;
2014-02-10 01:10:30 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_icon ( global_menu , p_idx , items [ p_idx ] . icon ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2023-03-31 19:17:59 +00:00
void PopupMenu : : set_item_icon_max_width ( int p_idx , int p_width ) {
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
if ( items [ p_idx ] . icon_max_width = = p_width ) {
return ;
}
items . write [ p_idx ] . icon_max_width = p_width ;
control - > queue_redraw ( ) ;
child_controls_changed ( ) ;
_menu_changed ( ) ;
}
2022-12-19 05:38:08 +00:00
void PopupMenu : : set_item_icon_modulate ( int p_idx , const Color & p_modulate ) {
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
if ( items [ p_idx ] . icon_modulate = = p_modulate ) {
return ;
}
items . write [ p_idx ] . icon_modulate = p_modulate ;
control - > queue_redraw ( ) ;
}
2014-02-10 01:10:30 +00:00
void PopupMenu : : set_item_checked ( int p_idx , bool p_checked ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . checked = = p_checked ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . checked = p_checked ;
2014-02-10 01:10:30 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_checked ( global_menu , p_idx , p_checked ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2020-05-14 12:29:06 +00:00
2019-05-09 09:21:49 +00:00
void PopupMenu : : set_item_id ( int p_idx , int p_id ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . id = = p_id ) {
return ;
}
2019-05-09 09:21:49 +00:00
items . write [ p_idx ] . id = p_id ;
2016-03-08 23:00:52 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_tag ( global_menu , p_idx , p_id ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2021-08-13 21:31:57 +00:00
void PopupMenu : : set_item_accelerator ( int p_idx , Key p_accel ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . accel = = p_accel ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . accel = p_accel ;
2020-09-03 11:22:16 +00:00
items . write [ p_idx ] . dirty = true ;
2014-02-10 01:10:30 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_accelerator ( global_menu , p_idx , p_accel ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
void PopupMenu : : set_item_metadata ( int p_idx , const Variant & p_meta ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . metadata = = p_meta ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . metadata = p_meta ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
void PopupMenu : : set_item_disabled ( int p_idx , bool p_disabled ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . disabled = = p_disabled ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . disabled = p_disabled ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_disabled ( global_menu , p_idx , p_disabled ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
void PopupMenu : : set_item_submenu ( int p_idx , const String & p_submenu ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
2023-11-28 16:33:04 +00:00
if ( items [ p_idx ] . submenu_name = = p_submenu ) {
2022-03-16 07:50:48 +00:00
return ;
}
2023-11-28 16:33:04 +00:00
PopupMenu * pm = Object : : cast_to < PopupMenu > ( get_node_or_null ( p_submenu ) ) ;
ERR_FAIL_NULL_MSG ( pm , vformat ( " Child PopupMenu \" %s \" does not exist. " , p_submenu ) ) ;
set_item_submenu_node ( p_idx , pm ) ;
}
void PopupMenu : : set_item_submenu_node ( int p_idx , PopupMenu * p_submenu ) {
ERR_FAIL_NULL ( p_submenu ) ;
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
if ( p_submenu - > get_parent ( ) ! = this ) {
ERR_FAIL_COND_MSG ( p_submenu - > get_parent ( ) ! = nullptr , vformat ( " The submenu \" %s \" already has a different parent. " , p_submenu - > get_name ( ) ) ) ;
add_child ( p_submenu ) ;
2024-02-05 03:33:29 +00:00
}
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
2023-07-11 08:17:35 +00:00
if ( items [ p_idx ] . submenu_bound ) {
2023-11-28 16:33:04 +00:00
PopupMenu * pm = items [ p_idx ] . submenu ;
2023-07-11 08:17:35 +00:00
if ( pm ) {
2024-01-19 17:41:01 +00:00
NativeMenu : : get_singleton ( ) - > set_item_submenu ( global_menu , p_idx , RID ( ) ) ;
2023-07-11 08:17:35 +00:00
pm - > unbind_global_menu ( ) ;
}
items . write [ p_idx ] . submenu_bound = false ;
}
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . submenu = p_submenu ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
PopupMenu * pm = items [ p_idx ] . submenu ;
if ( pm ) {
RID submenu_rid = pm - > bind_global_menu ( ) ;
NativeMenu : : get_singleton ( ) - > set_item_submenu ( global_menu , p_idx , submenu_rid ) ;
2023-11-28 16:33:04 +00:00
items . write [ p_idx ] . submenu_bound = true ;
2023-07-11 08:17:35 +00:00
}
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2016-10-01 13:50:54 +00:00
void PopupMenu : : toggle_item_checked ( int p_idx ) {
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . checked = ! items [ p_idx ] . checked ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_checked ( global_menu , p_idx , items [ p_idx ] . checked ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2016-10-01 13:50:54 +00:00
}
2014-02-10 01:10:30 +00:00
String PopupMenu : : get_item_text ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , " " ) ;
return items [ p_idx ] . text ;
}
2017-07-14 08:45:24 +00:00
2023-06-27 14:22:58 +00:00
String PopupMenu : : get_item_xl_text ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , " " ) ;
return items [ p_idx ] . xl_text ;
}
2023-05-24 13:58:50 +00:00
Control : : TextDirection PopupMenu : : get_item_text_direction ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , Control : : TEXT_DIRECTION_INHERITED ) ;
return items [ p_idx ] . text_direction ;
2020-09-03 11:22:16 +00:00
}
2023-05-24 13:58:50 +00:00
String PopupMenu : : get_item_language ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , " " ) ;
return items [ p_idx ] . language ;
2020-09-03 11:22:16 +00:00
}
2017-07-14 08:45:24 +00:00
int PopupMenu : : get_item_idx_from_text ( const String & text ) const {
for ( int idx = 0 ; idx < items . size ( ) ; idx + + ) {
2020-05-14 14:41:43 +00:00
if ( items [ idx ] . text = = text ) {
2017-07-14 08:45:24 +00:00
return idx ;
2020-05-14 14:41:43 +00:00
}
2017-07-14 08:45:24 +00:00
}
return - 1 ;
}
2019-06-11 18:43:37 +00:00
Ref < Texture2D > PopupMenu : : get_item_icon ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , Ref < Texture2D > ( ) ) ;
2014-02-10 01:10:30 +00:00
return items [ p_idx ] . icon ;
}
2023-03-31 19:17:59 +00:00
int PopupMenu : : get_item_icon_max_width ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , 0 ) ;
return items [ p_idx ] . icon_max_width ;
}
2022-12-19 05:38:08 +00:00
Color PopupMenu : : get_item_icon_modulate ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , Color ( ) ) ;
return items [ p_idx ] . icon_modulate ;
}
2021-08-13 21:31:57 +00:00
Key PopupMenu : : get_item_accelerator ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , Key : : NONE ) ;
2014-02-10 01:10:30 +00:00
return items [ p_idx ] . accel ;
}
Variant PopupMenu : : get_item_metadata ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , Variant ( ) ) ;
return items [ p_idx ] . metadata ;
}
bool PopupMenu : : is_item_disabled ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , false ) ;
return items [ p_idx ] . disabled ;
}
bool PopupMenu : : is_item_checked ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , false ) ;
return items [ p_idx ] . checked ;
}
2017-08-07 10:17:31 +00:00
int PopupMenu : : get_item_id ( int p_idx ) const {
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , 0 ) ;
2019-05-09 09:21:49 +00:00
return items [ p_idx ] . id ;
2014-02-10 01:10:30 +00:00
}
2019-05-09 09:21:49 +00:00
int PopupMenu : : get_item_index ( int p_id ) const {
2014-02-10 01:10:30 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2020-05-14 14:41:43 +00:00
if ( items [ i ] . id = = p_id ) {
2014-02-10 01:10:30 +00:00
return i ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
}
return - 1 ;
}
String PopupMenu : : get_item_submenu ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , " " ) ;
2023-11-28 16:33:04 +00:00
return items [ p_idx ] . submenu_name ;
}
PopupMenu * PopupMenu : : get_item_submenu_node ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , nullptr ) ;
2014-02-10 01:10:30 +00:00
return items [ p_idx ] . submenu ;
}
String PopupMenu : : get_item_tooltip ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , " " ) ;
return items [ p_idx ] . tooltip ;
}
2020-09-09 19:53:24 +00:00
Ref < Shortcut > PopupMenu : : get_item_shortcut ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , Ref < Shortcut > ( ) ) ;
2016-06-05 00:31:29 +00:00
return items [ p_idx ] . shortcut ;
}
2022-08-01 09:28:16 +00:00
int PopupMenu : : get_item_indent ( int p_idx ) const {
2022-06-15 05:04:42 +00:00
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , 0 ) ;
2022-08-01 09:28:16 +00:00
return items [ p_idx ] . indent ;
}
int PopupMenu : : get_item_max_states ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , - 1 ) ;
return items [ p_idx ] . max_states ;
2022-06-15 05:04:42 +00:00
}
2017-11-22 21:29:27 +00:00
int PopupMenu : : get_item_state ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , - 1 ) ;
return items [ p_idx ] . state ;
}
2014-02-10 01:10:30 +00:00
void PopupMenu : : set_item_as_separator ( int p_idx , bool p_separator ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . separator = = p_separator ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . separator = p_separator ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2014-02-10 01:10:30 +00:00
}
bool PopupMenu : : is_item_separator ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , false ) ;
return items [ p_idx ] . separator ;
}
void PopupMenu : : set_item_as_checkable ( int p_idx , bool p_checkable ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
int type = ( int ) ( p_checkable ? Item : : CHECKABLE_TYPE_CHECK_BOX : Item : : CHECKABLE_TYPE_NONE ) ;
if ( type = = items [ p_idx ] . checkable_type ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . checkable_type = p_checkable ? Item : : CHECKABLE_TYPE_CHECK_BOX : Item : : CHECKABLE_TYPE_NONE ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_checkable ( global_menu , p_idx , p_checkable ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2018-03-23 20:55:40 +00:00
}
void PopupMenu : : set_item_as_radio_checkable ( int p_idx , bool p_radio_checkable ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2018-03-23 20:55:40 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
int type = ( int ) ( p_radio_checkable ? Item : : CHECKABLE_TYPE_RADIO_BUTTON : Item : : CHECKABLE_TYPE_NONE ) ;
if ( type = = items [ p_idx ] . checkable_type ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . checkable_type = p_radio_checkable ? Item : : CHECKABLE_TYPE_RADIO_BUTTON : Item : : CHECKABLE_TYPE_NONE ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_radio_checkable ( global_menu , p_idx , p_radio_checkable ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
void PopupMenu : : set_item_tooltip ( int p_idx , const String & p_tooltip ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2014-02-10 01:10:30 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . tooltip = = p_tooltip ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . tooltip = p_tooltip ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_tooltip ( global_menu , p_idx , p_tooltip ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2020-09-09 19:53:24 +00:00
void PopupMenu : : set_item_shortcut ( int p_idx , const Ref < Shortcut > & p_shortcut , bool p_global ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2016-06-05 00:31:29 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . shortcut = = p_shortcut & & items [ p_idx ] . shortcut_is_global = = p_global & & items [ p_idx ] . shortcut . is_valid ( ) = = p_shortcut . is_valid ( ) ) {
return ;
}
2016-06-05 00:31:29 +00:00
if ( items [ p_idx ] . shortcut . is_valid ( ) ) {
_unref_shortcut ( items [ p_idx ] . shortcut ) ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . shortcut = p_shortcut ;
items . write [ p_idx ] . shortcut_is_global = p_global ;
2020-09-03 11:22:16 +00:00
items . write [ p_idx ] . dirty = true ;
2016-06-05 00:31:29 +00:00
if ( items [ p_idx ] . shortcut . is_valid ( ) ) {
_ref_shortcut ( items [ p_idx ] . shortcut ) ;
}
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
nmenu - > set_item_accelerator ( global_menu , p_idx , Key : : NONE ) ;
2023-07-11 08:17:35 +00:00
if ( ! items [ p_idx ] . shortcut_is_disabled & & items [ p_idx ] . shortcut . is_valid ( ) & & items [ p_idx ] . shortcut - > has_valid_event ( ) ) {
Array events = items [ p_idx ] . shortcut - > get_events ( ) ;
for ( int j = 0 ; j < events . size ( ) ; j + + ) {
Ref < InputEventKey > ie = events [ j ] ;
2023-10-06 06:36:42 +00:00
if ( ie . is_valid ( ) & & _set_item_accelerator ( p_idx , ie ) ) {
2023-07-11 08:17:35 +00:00
break ;
}
}
2023-10-06 08:16:41 +00:00
if ( p_global ) {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_key_callback ( global_menu , p_idx , callable_mp ( this , & PopupMenu : : activate_item ) ) ;
2023-10-06 08:16:41 +00:00
} else {
2024-01-19 17:41:01 +00:00
nmenu - > set_item_key_callback ( global_menu , p_idx , Callable ( ) ) ;
2023-10-06 08:16:41 +00:00
}
2023-07-11 08:17:35 +00:00
}
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2016-06-05 00:31:29 +00:00
}
2022-08-01 09:28:16 +00:00
void PopupMenu : : set_item_indent ( int p_idx , int p_indent ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2016-09-17 22:01:11 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items . write [ p_idx ] . indent = = p_indent ) {
return ;
}
2022-08-01 09:28:16 +00:00
items . write [ p_idx ] . indent = p_indent ;
2022-03-16 07:50:48 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_indentation_level ( global_menu , p_idx , p_indent ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2016-09-17 22:01:11 +00:00
}
2024-01-20 06:10:28 +00:00
void PopupMenu : : set_item_max_states ( int p_idx , int p_max_states ) {
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
if ( items [ p_idx ] . max_states = = p_max_states ) {
return ;
}
items . write [ p_idx ] . max_states = p_max_states ;
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_max_states ( global_menu , p_idx , p_max_states ) ;
2024-01-20 06:10:28 +00:00
}
control - > queue_redraw ( ) ;
_menu_changed ( ) ;
}
2017-12-12 02:37:37 +00:00
void PopupMenu : : set_item_multistate ( int p_idx , int p_state ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2017-11-22 21:29:27 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . state = = p_state ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . state = p_state ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_state ( global_menu , p_idx , p_state ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2017-11-22 21:29:27 +00:00
}
2018-06-07 15:46:14 +00:00
void PopupMenu : : set_item_shortcut_disabled ( int p_idx , bool p_disabled ) {
2022-03-12 00:06:45 +00:00
if ( p_idx < 0 ) {
p_idx + = get_item_count ( ) ;
}
2018-06-07 15:46:14 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-03-16 07:50:48 +00:00
if ( items [ p_idx ] . shortcut_is_disabled = = p_disabled ) {
return ;
}
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . shortcut_is_disabled = p_disabled ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
nmenu - > set_item_accelerator ( global_menu , p_idx , Key : : NONE ) ;
2023-07-11 08:17:35 +00:00
if ( ! items [ p_idx ] . shortcut_is_disabled & & items [ p_idx ] . shortcut . is_valid ( ) & & items [ p_idx ] . shortcut - > has_valid_event ( ) ) {
Array events = items [ p_idx ] . shortcut - > get_events ( ) ;
for ( int j = 0 ; j < events . size ( ) ; j + + ) {
Ref < InputEventKey > ie = events [ j ] ;
2023-10-06 06:36:42 +00:00
if ( ie . is_valid ( ) & & _set_item_accelerator ( p_idx , ie ) ) {
2023-07-11 08:17:35 +00:00
break ;
}
}
}
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2018-06-07 15:46:14 +00:00
}
2017-12-12 02:37:37 +00:00
void PopupMenu : : toggle_item_multistate ( int p_idx ) {
2017-11-22 21:29:27 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
if ( 0 > = items [ p_idx ] . max_states ) {
return ;
}
2018-07-25 01:11:03 +00:00
+ + items . write [ p_idx ] . state ;
2020-05-14 14:41:43 +00:00
if ( items . write [ p_idx ] . max_states < = items [ p_idx ] . state ) {
2018-07-25 01:11:03 +00:00
items . write [ p_idx ] . state = 0 ;
2020-05-14 14:41:43 +00:00
}
2017-11-22 21:29:27 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > set_item_state ( global_menu , p_idx , items [ p_idx ] . state ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2017-11-22 21:29:27 +00:00
}
2014-02-10 01:10:30 +00:00
bool PopupMenu : : is_item_checkable ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , false ) ;
2018-03-23 20:55:40 +00:00
return items [ p_idx ] . checkable_type ;
}
bool PopupMenu : : is_item_radio_checkable ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , false ) ;
return items [ p_idx ] . checkable_type = = Item : : CHECKABLE_TYPE_RADIO_BUTTON ;
2014-02-10 01:10:30 +00:00
}
2022-08-01 09:28:16 +00:00
bool PopupMenu : : is_item_shortcut_global ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , false ) ;
return items [ p_idx ] . shortcut_is_global ;
}
2018-06-07 15:46:14 +00:00
bool PopupMenu : : is_item_shortcut_disabled ( int p_idx ) const {
ERR_FAIL_INDEX_V ( p_idx , items . size ( ) , false ) ;
return items [ p_idx ] . shortcut_is_disabled ;
}
2022-09-06 13:51:14 +00:00
void PopupMenu : : set_focused_item ( int p_idx ) {
2022-08-27 16:32:45 +00:00
if ( p_idx ! = - 1 ) {
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
}
2022-03-16 07:50:48 +00:00
if ( mouse_over = = p_idx ) {
return ;
}
2020-10-15 23:04:30 +00:00
mouse_over = p_idx ;
2022-08-27 16:32:45 +00:00
if ( mouse_over ! = - 1 ) {
scroll_to_item ( mouse_over ) ;
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-10-15 23:04:30 +00:00
}
2022-09-06 13:51:14 +00:00
int PopupMenu : : get_focused_item ( ) const {
2020-05-06 20:27:35 +00:00
return mouse_over ;
}
2021-11-03 02:08:58 +00:00
void PopupMenu : : set_item_count ( int p_count ) {
ERR_FAIL_COND ( p_count < 0 ) ;
2021-11-05 22:28:04 +00:00
int prev_size = items . size ( ) ;
2022-03-16 07:50:48 +00:00
if ( prev_size = = p_count ) {
return ;
}
2024-01-19 17:41:01 +00:00
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
bool is_global = global_menu . is_valid ( ) ;
2023-07-11 08:17:35 +00:00
if ( is_global & & prev_size > p_count ) {
for ( int i = prev_size - 1 ; i > = p_count ; i - - ) {
2024-01-19 17:41:01 +00:00
nmenu - > remove_item ( global_menu , i ) ;
2023-07-11 08:17:35 +00:00
}
}
2021-11-03 02:08:58 +00:00
items . resize ( p_count ) ;
2021-11-05 22:28:04 +00:00
if ( prev_size < p_count ) {
for ( int i = prev_size ; i < p_count ; i + + ) {
items . write [ i ] . id = i ;
2023-07-11 08:17:35 +00:00
if ( is_global ) {
2024-01-19 17:41:01 +00:00
nmenu - > add_item ( global_menu , String ( ) , callable_mp ( this , & PopupMenu : : activate_item ) , Callable ( ) , i ) ;
2023-07-11 08:17:35 +00:00
}
2021-11-05 22:28:04 +00:00
}
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2021-11-03 02:08:58 +00:00
child_controls_changed ( ) ;
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2021-11-03 02:08:58 +00:00
}
2014-02-10 01:10:30 +00:00
int PopupMenu : : get_item_count ( ) const {
return items . size ( ) ;
}
2023-05-24 13:58:50 +00:00
void PopupMenu : : scroll_to_item ( int p_idx ) {
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
2022-02-06 02:47:00 +00:00
2023-08-04 17:24:20 +00:00
// Calculate the position of the item relative to the visible area.
int item_y = items [ p_idx ] . _ofs_cache ;
int visible_height = scroll_container - > get_size ( ) . height ;
int relative_y = item_y - scroll_container - > get_v_scroll ( ) ;
// If item is not fully visible, adjust scroll.
if ( relative_y < 0 ) {
scroll_container - > set_v_scroll ( item_y ) ;
} else if ( relative_y + items [ p_idx ] . _height_cache > visible_height ) {
scroll_container - > set_v_scroll ( item_y + items [ p_idx ] . _height_cache - visible_height ) ;
2022-02-06 02:47:00 +00:00
}
}
2024-03-07 18:42:24 +00:00
void PopupMenu : : set_prefer_native_menu ( bool p_enabled ) {
if ( prefer_native ! = p_enabled ) {
prefer_native = p_enabled ;
if ( prefer_native ) {
bind_global_menu ( ) ;
} else {
unbind_global_menu ( ) ;
}
}
}
bool PopupMenu : : is_prefer_native_menu ( ) const {
return prefer_native ;
}
2017-05-20 15:38:03 +00:00
bool PopupMenu : : activate_item_by_event ( const Ref < InputEvent > & p_event , bool p_for_global_only ) {
2023-10-25 15:45:01 +00:00
ERR_FAIL_COND_V ( p_event . is_null ( ) , false ) ;
2021-08-13 21:31:57 +00:00
Key code = Key : : NONE ;
2017-05-20 15:38:03 +00:00
Ref < InputEventKey > k = p_event ;
if ( k . is_valid ( ) ) {
2018-04-05 17:59:35 +00:00
code = k - > get_keycode ( ) ;
2021-08-13 21:31:57 +00:00
if ( code = = Key : : NONE ) {
2021-06-20 17:12:33 +00:00
code = ( Key ) k - > get_unicode ( ) ;
2020-05-14 14:41:43 +00:00
}
2021-04-24 20:33:50 +00:00
if ( k - > is_ctrl_pressed ( ) ) {
2021-08-13 21:31:57 +00:00
code | = KeyModifierMask : : CTRL ;
2020-05-14 14:41:43 +00:00
}
2021-04-24 20:33:50 +00:00
if ( k - > is_alt_pressed ( ) ) {
2021-08-13 21:31:57 +00:00
code | = KeyModifierMask : : ALT ;
2020-05-14 14:41:43 +00:00
}
2021-04-24 20:33:50 +00:00
if ( k - > is_meta_pressed ( ) ) {
2021-08-13 21:31:57 +00:00
code | = KeyModifierMask : : META ;
2020-05-14 14:41:43 +00:00
}
2021-04-24 20:33:50 +00:00
if ( k - > is_shift_pressed ( ) ) {
2021-08-13 21:31:57 +00:00
code | = KeyModifierMask : : SHIFT ;
2020-05-14 14:41:43 +00:00
}
2016-06-05 00:31:29 +00:00
}
2018-09-11 04:44:19 +00:00
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
2020-02-23 21:23:34 +00:00
if ( is_item_disabled ( i ) | | items [ i ] . shortcut_is_disabled | | ( ! items [ i ] . allow_echo & & p_event - > is_echo ( ) ) ) {
2016-04-06 12:37:57 +00:00
continue ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
2021-08-03 15:27:45 +00:00
if ( items [ i ] . shortcut . is_valid ( ) & & items [ i ] . shortcut - > matches_event ( p_event ) & & ( items [ i ] . shortcut_is_global | | ! p_for_global_only ) ) {
2016-06-05 00:31:29 +00:00
activate_item ( i ) ;
return true ;
}
2021-08-13 21:31:57 +00:00
if ( code ! = Key : : NONE & & items [ i ] . accel = = code ) {
2016-04-06 12:37:57 +00:00
activate_item ( i ) ;
return true ;
}
2023-11-28 16:33:04 +00:00
if ( items [ i ] . submenu ) {
if ( items [ i ] . submenu - > activate_item_by_event ( p_event , p_for_global_only ) ) {
2016-04-06 12:37:57 +00:00
return true ;
}
}
2014-02-10 01:10:30 +00:00
}
2016-04-06 12:37:57 +00:00
return false ;
2014-02-10 01:10:30 +00:00
}
2023-07-11 08:17:35 +00:00
void PopupMenu : : _about_to_popup ( ) {
ERR_MAIN_THREAD_GUARD ;
emit_signal ( SNAME ( " about_to_popup " ) ) ;
}
void PopupMenu : : _about_to_close ( ) {
ERR_MAIN_THREAD_GUARD ;
emit_signal ( SNAME ( " popup_hide " ) ) ;
}
2023-05-24 13:58:50 +00:00
void PopupMenu : : activate_item ( int p_idx ) {
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
ERR_FAIL_COND ( items [ p_idx ] . separator ) ;
int id = items [ p_idx ] . id > = 0 ? items [ p_idx ] . id : p_idx ;
2015-08-12 20:24:21 +00:00
2018-08-13 19:55:26 +00:00
//hide all parent PopupMenus
2015-08-12 20:24:21 +00:00
Node * next = get_parent ( ) ;
2017-08-24 20:58:51 +00:00
PopupMenu * pop = Object : : cast_to < PopupMenu > ( next ) ;
2015-08-12 20:24:21 +00:00
while ( pop ) {
2016-12-23 14:43:45 +00:00
// We close all parents that are chained together,
// with hide_on_item_selection enabled
2017-11-22 21:29:27 +00:00
2023-05-24 13:58:50 +00:00
if ( items [ p_idx ] . checkable_type ) {
2020-05-14 14:41:43 +00:00
if ( ! hide_on_checkable_item_selection | | ! pop - > is_hide_on_checkable_item_selection ( ) ) {
2017-11-22 21:29:27 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2023-05-24 13:58:50 +00:00
} else if ( 0 < items [ p_idx ] . max_states ) {
2020-05-14 14:41:43 +00:00
if ( ! hide_on_multistate_item_selection | | ! pop - > is_hide_on_multistate_item_selection ( ) ) {
2017-11-22 21:29:27 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
} else if ( ! hide_on_item_selection | | ! pop - > is_hide_on_item_selection ( ) ) {
2016-12-23 14:43:45 +00:00
break ;
2020-05-14 14:41:43 +00:00
}
2017-11-22 21:29:27 +00:00
pop - > hide ( ) ;
next = next - > get_parent ( ) ;
pop = Object : : cast_to < PopupMenu > ( next ) ;
2016-12-23 14:43:45 +00:00
}
2017-11-22 21:29:27 +00:00
2016-12-23 14:43:45 +00:00
// Hides popup by default; unless otherwise specified
2017-04-14 11:49:00 +00:00
// by using set_hide_on_item_selection and set_hide_on_checkable_item_selection
2017-11-22 21:29:27 +00:00
2018-08-16 19:44:18 +00:00
bool need_hide = true ;
2023-05-24 13:58:50 +00:00
if ( items [ p_idx ] . checkable_type ) {
2020-05-14 14:41:43 +00:00
if ( ! hide_on_checkable_item_selection ) {
2018-08-16 19:44:18 +00:00
need_hide = false ;
2020-05-14 14:41:43 +00:00
}
2023-05-24 13:58:50 +00:00
} else if ( 0 < items [ p_idx ] . max_states ) {
2020-05-14 14:41:43 +00:00
if ( ! hide_on_multistate_item_selection ) {
2018-08-16 19:44:18 +00:00
need_hide = false ;
2020-05-14 14:41:43 +00:00
}
} else if ( ! hide_on_item_selection ) {
2018-08-16 19:44:18 +00:00
need_hide = false ;
2020-05-14 14:41:43 +00:00
}
2017-11-22 21:29:27 +00:00
2018-08-16 19:44:18 +00:00
if ( need_hide ) {
hide ( ) ;
}
2023-02-24 19:17:05 +00:00
emit_signal ( SNAME ( " id_pressed " ) , id ) ;
2023-05-24 13:58:50 +00:00
emit_signal ( SNAME ( " index_pressed " ) , p_idx ) ;
2014-02-10 01:10:30 +00:00
}
void PopupMenu : : remove_item ( int p_idx ) {
2016-06-05 00:31:29 +00:00
ERR_FAIL_INDEX ( p_idx , items . size ( ) ) ;
if ( items [ p_idx ] . shortcut . is_valid ( ) ) {
_unref_shortcut ( items [ p_idx ] . shortcut ) ;
}
2021-07-03 22:17:03 +00:00
items . remove_at ( p_idx ) ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > remove_item ( global_menu , p_idx ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2020-12-27 12:18:47 +00:00
void PopupMenu : : add_separator ( const String & p_text , int p_id ) {
2014-02-10 01:10:30 +00:00
Item sep ;
sep . separator = true ;
2020-12-27 12:18:47 +00:00
sep . id = p_id ;
2021-12-09 09:42:46 +00:00
if ( ! p_text . is_empty ( ) ) {
2018-07-14 21:15:42 +00:00
sep . text = p_text ;
2021-05-27 17:31:33 +00:00
sep . xl_text = atr ( p_text ) ;
2018-07-14 21:15:42 +00:00
}
2014-02-10 01:10:30 +00:00
items . push_back ( sep ) ;
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu : : get_singleton ( ) - > add_separator ( global_menu ) ;
2023-07-11 08:17:35 +00:00
}
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2016-03-08 23:00:52 +00:00
2023-07-27 17:50:31 +00:00
void PopupMenu : : clear ( bool p_free_submenus ) {
for ( const Item & I : items ) {
if ( I . shortcut . is_valid ( ) ) {
_unref_shortcut ( I . shortcut ) ;
}
2023-11-28 16:33:04 +00:00
if ( p_free_submenus & & I . submenu ) {
remove_child ( I . submenu ) ;
I . submenu - > queue_free ( ) ;
2016-06-05 00:31:29 +00:00
}
}
2023-07-11 08:17:35 +00:00
2024-01-19 17:41:01 +00:00
if ( global_menu . is_valid ( ) ) {
NativeMenu * nmenu = NativeMenu : : get_singleton ( ) ;
2024-01-16 13:10:40 +00:00
for ( int i = items . size ( ) - 1 ; i > = 0 ; i - - ) {
2023-07-11 08:17:35 +00:00
Item & item = items . write [ i ] ;
2023-11-28 16:33:04 +00:00
if ( item . submenu ) {
item . submenu - > unbind_global_menu ( ) ;
2023-07-11 08:17:35 +00:00
item . submenu_bound = false ;
}
2024-01-19 17:41:01 +00:00
nmenu - > remove_item ( global_menu , i ) ;
2023-07-11 08:17:35 +00:00
}
}
2014-02-10 01:10:30 +00:00
items . clear ( ) ;
2023-07-11 08:17:35 +00:00
2015-11-13 10:16:55 +00:00
mouse_over = - 1 ;
2022-08-13 21:21:24 +00:00
control - > queue_redraw ( ) ;
2020-03-12 12:37:40 +00:00
child_controls_changed ( ) ;
2021-11-03 02:08:58 +00:00
notify_property_list_changed ( ) ;
2022-08-01 09:28:16 +00:00
_menu_changed ( ) ;
2014-02-10 01:10:30 +00:00
}
2016-06-05 00:31:29 +00:00
2020-09-09 19:53:24 +00:00
void PopupMenu : : _ref_shortcut ( Ref < Shortcut > p_sc ) {
2016-06-05 00:31:29 +00:00
if ( ! shortcut_refcount . has ( p_sc ) ) {
shortcut_refcount [ p_sc ] = 1 ;
2023-07-03 19:29:37 +00:00
p_sc - > connect_changed ( callable_mp ( this , & PopupMenu : : _shortcut_changed ) ) ;
2016-06-05 00:31:29 +00:00
} else {
shortcut_refcount [ p_sc ] + = 1 ;
}
}
2020-09-09 19:53:24 +00:00
void PopupMenu : : _unref_shortcut ( Ref < Shortcut > p_sc ) {
2016-06-05 00:31:29 +00:00
ERR_FAIL_COND ( ! shortcut_refcount . has ( p_sc ) ) ;
shortcut_refcount [ p_sc ] - - ;
if ( shortcut_refcount [ p_sc ] = = 0 ) {
2023-07-03 19:29:37 +00:00
p_sc - > disconnect_changed ( callable_mp ( this , & PopupMenu : : _shortcut_changed ) ) ;
2016-06-05 00:31:29 +00:00
shortcut_refcount . erase ( p_sc ) ;
}
}
2022-09-26 13:21:04 +00:00
void PopupMenu : : _shortcut_changed ( ) {
for ( int i = 0 ; i < items . size ( ) ; i + + ) {
items . write [ i ] . dirty = true ;
}
control - > queue_redraw ( ) ;
}
2016-12-23 14:43:45 +00:00
// Hide on item selection determines whether or not the popup will close after item selection
void PopupMenu : : set_hide_on_item_selection ( bool p_enabled ) {
hide_on_item_selection = p_enabled ;
}
2017-11-22 21:29:27 +00:00
bool PopupMenu : : is_hide_on_item_selection ( ) const {
2016-12-23 14:43:45 +00:00
return hide_on_item_selection ;
}
2014-02-10 01:10:30 +00:00
2017-04-14 11:49:00 +00:00
void PopupMenu : : set_hide_on_checkable_item_selection ( bool p_enabled ) {
hide_on_checkable_item_selection = p_enabled ;
}
2017-11-22 21:29:27 +00:00
bool PopupMenu : : is_hide_on_checkable_item_selection ( ) const {
2017-04-14 11:49:00 +00:00
return hide_on_checkable_item_selection ;
}
2017-12-12 02:37:37 +00:00
void PopupMenu : : set_hide_on_multistate_item_selection ( bool p_enabled ) {
hide_on_multistate_item_selection = p_enabled ;
2017-11-22 21:29:27 +00:00
}
2017-12-12 02:37:37 +00:00
bool PopupMenu : : is_hide_on_multistate_item_selection ( ) const {
return hide_on_multistate_item_selection ;
2017-11-22 21:29:27 +00:00
}
2018-07-09 12:16:06 +00:00
void PopupMenu : : set_submenu_popup_delay ( float p_time ) {
2020-05-14 14:41:43 +00:00
if ( p_time < = 0 ) {
2018-07-09 12:16:06 +00:00
p_time = 0.01 ;
2020-05-14 14:41:43 +00:00
}
2018-07-09 12:16:06 +00:00
submenu_timer - > set_wait_time ( p_time ) ;
}
float PopupMenu : : get_submenu_popup_delay ( ) const {
return submenu_timer - > get_wait_time ( ) ;
}
2019-05-11 16:32:53 +00:00
void PopupMenu : : set_allow_search ( bool p_allow ) {
allow_search = p_allow ;
}
bool PopupMenu : : get_allow_search ( ) const {
return allow_search ;
}
2014-02-10 01:10:30 +00:00
String PopupMenu : : get_tooltip ( const Point2 & p_pos ) const {
int over = _get_mouse_over ( p_pos ) ;
2020-05-14 14:41:43 +00:00
if ( over < 0 | | over > = items . size ( ) ) {
2014-02-10 01:10:30 +00:00
return " " ;
2020-05-14 14:41:43 +00:00
}
2014-02-10 01:10:30 +00:00
return items [ over ] . tooltip ;
}
void PopupMenu : : add_autohide_area ( const Rect2 & p_area ) {
autohide_areas . push_back ( p_area ) ;
}
void PopupMenu : : clear_autohide_areas ( ) {
autohide_areas . clear ( ) ;
}
2020-03-20 02:32:09 +00:00
void PopupMenu : : take_mouse_focus ( ) {
ERR_FAIL_COND ( ! is_inside_tree ( ) ) ;
if ( get_parent ( ) ) {
get_parent ( ) - > get_viewport ( ) - > pass_mouse_focus_to ( this , control ) ;
}
}
2021-11-03 02:08:58 +00:00
bool PopupMenu : : _set ( const StringName & p_name , const Variant & p_value ) {
2024-02-11 11:09:55 +00:00
if ( property_helper . property_set_value ( p_name , p_value ) ) {
return true ;
2021-11-03 02:08:58 +00:00
}
2024-02-11 11:09:55 +00:00
2021-11-03 02:08:58 +00:00
# ifndef DISABLE_DEPRECATED
// Compatibility.
if ( p_name = = " items " ) {
Array arr = p_value ;
ERR_FAIL_COND_V ( arr . size ( ) % 10 , false ) ;
clear ( ) ;
for ( int i = 0 ; i < arr . size ( ) ; i + = 10 ) {
String text = arr [ i + 0 ] ;
Ref < Texture2D > icon = arr [ i + 1 ] ;
// For compatibility, use false/true for no/checkbox and integers for other values
bool checkable = arr [ i + 2 ] ;
bool radio_checkable = ( int ) arr [ i + 2 ] = = Item : : CHECKABLE_TYPE_RADIO_BUTTON ;
bool checked = arr [ i + 3 ] ;
bool disabled = arr [ i + 4 ] ;
int id = arr [ i + 5 ] ;
int accel = arr [ i + 6 ] ;
Variant meta = arr [ i + 7 ] ;
String subm = arr [ i + 8 ] ;
bool sep = arr [ i + 9 ] ;
int idx = get_item_count ( ) ;
add_item ( text , id ) ;
set_item_icon ( idx , icon ) ;
if ( checkable ) {
if ( radio_checkable ) {
set_item_as_radio_checkable ( idx , true ) ;
} else {
set_item_as_checkable ( idx , true ) ;
}
}
set_item_checked ( idx , checked ) ;
set_item_disabled ( idx , disabled ) ;
set_item_id ( idx , id ) ;
set_item_metadata ( idx , meta ) ;
set_item_as_separator ( idx , sep ) ;
2021-08-13 21:31:57 +00:00
set_item_accelerator ( idx , ( Key ) accel ) ;
2021-11-03 02:08:58 +00:00
set_item_submenu ( idx , subm ) ;
}
}
# endif
return false ;
}
2014-02-10 01:10:30 +00:00
void PopupMenu : : _bind_methods ( ) {
2023-09-13 18:10:09 +00:00
ClassDB : : bind_method ( D_METHOD ( " activate_item_by_event " , " event " , " for_global_only " ) , & PopupMenu : : activate_item_by_event , DEFVAL ( false ) ) ;
2024-03-07 18:42:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_prefer_native_menu " , " enabled " ) , & PopupMenu : : set_prefer_native_menu ) ;
ClassDB : : bind_method ( D_METHOD ( " is_prefer_native_menu " ) , & PopupMenu : : is_prefer_native_menu ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_item " , " label " , " id " , " accel " ) , & PopupMenu : : add_item , DEFVAL ( - 1 ) , DEFVAL ( 0 ) ) ;
2019-10-08 06:28:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_icon_item " , " texture " , " label " , " id " , " accel " ) , & PopupMenu : : add_icon_item , DEFVAL ( - 1 ) , DEFVAL ( 0 ) ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_check_item " , " label " , " id " , " accel " ) , & PopupMenu : : add_check_item , DEFVAL ( - 1 ) , DEFVAL ( 0 ) ) ;
2019-10-08 06:28:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_icon_check_item " , " texture " , " label " , " id " , " accel " ) , & PopupMenu : : add_icon_check_item , DEFVAL ( - 1 ) , DEFVAL ( 0 ) ) ;
2018-03-23 20:55:40 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_radio_check_item " , " label " , " id " , " accel " ) , & PopupMenu : : add_radio_check_item , DEFVAL ( - 1 ) , DEFVAL ( 0 ) ) ;
2019-10-08 06:28:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_icon_radio_check_item " , " texture " , " label " , " id " , " accel " ) , & PopupMenu : : add_icon_radio_check_item , DEFVAL ( - 1 ) , DEFVAL ( 0 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " add_multistate_item " , " label " , " max_states " , " default_state " , " id " , " accel " ) , & PopupMenu : : add_multistate_item , DEFVAL ( 0 ) , DEFVAL ( - 1 ) , DEFVAL ( 0 ) ) ;
2017-03-05 15:44:50 +00:00
2020-02-23 21:23:34 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_shortcut " , " shortcut " , " id " , " global " , " allow_echo " ) , & PopupMenu : : add_shortcut , DEFVAL ( - 1 ) , DEFVAL ( false ) , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " add_icon_shortcut " , " texture " , " shortcut " , " id " , " global " , " allow_echo " ) , & PopupMenu : : add_icon_shortcut , DEFVAL ( - 1 ) , DEFVAL ( false ) , DEFVAL ( false ) ) ;
2017-08-09 11:19:41 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_check_shortcut " , " shortcut " , " id " , " global " ) , & PopupMenu : : add_check_shortcut , DEFVAL ( - 1 ) , DEFVAL ( false ) ) ;
2019-10-08 06:28:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_icon_check_shortcut " , " texture " , " shortcut " , " id " , " global " ) , & PopupMenu : : add_icon_check_shortcut , DEFVAL ( - 1 ) , DEFVAL ( false ) ) ;
2018-03-23 20:55:40 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_radio_check_shortcut " , " shortcut " , " id " , " global " ) , & PopupMenu : : add_radio_check_shortcut , DEFVAL ( - 1 ) , DEFVAL ( false ) ) ;
2019-10-08 06:28:26 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_icon_radio_check_shortcut " , " texture " , " shortcut " , " id " , " global " ) , & PopupMenu : : add_icon_radio_check_shortcut , DEFVAL ( - 1 ) , DEFVAL ( false ) ) ;
ClassDB : : bind_method ( D_METHOD ( " add_submenu_item " , " label " , " submenu " , " id " ) , & PopupMenu : : add_submenu_item , DEFVAL ( - 1 ) ) ;
2023-11-28 16:33:04 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_submenu_node_item " , " label " , " submenu " , " id " ) , & PopupMenu : : add_submenu_node_item , DEFVAL ( - 1 ) ) ;
2017-03-05 15:44:50 +00:00
2021-12-08 12:02:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_text " , " index " , " text " ) , & PopupMenu : : set_item_text ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_text_direction " , " index " , " direction " ) , & PopupMenu : : set_item_text_direction ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_language " , " index " , " language " ) , & PopupMenu : : set_item_language ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_icon " , " index " , " icon " ) , & PopupMenu : : set_item_icon ) ;
2023-03-31 19:17:59 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_icon_max_width " , " index " , " width " ) , & PopupMenu : : set_item_icon_max_width ) ;
2022-12-19 05:38:08 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_icon_modulate " , " index " , " modulate " ) , & PopupMenu : : set_item_icon_modulate ) ;
2021-12-08 12:02:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_checked " , " index " , " checked " ) , & PopupMenu : : set_item_checked ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_id " , " index " , " id " ) , & PopupMenu : : set_item_id ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_accelerator " , " index " , " accel " ) , & PopupMenu : : set_item_accelerator ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_metadata " , " index " , " metadata " ) , & PopupMenu : : set_item_metadata ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_disabled " , " index " , " disabled " ) , & PopupMenu : : set_item_disabled ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_submenu " , " index " , " submenu " ) , & PopupMenu : : set_item_submenu ) ;
2023-11-28 16:33:04 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_submenu_node " , " index " , " submenu " ) , & PopupMenu : : set_item_submenu_node ) ;
2021-12-08 12:02:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_as_separator " , " index " , " enable " ) , & PopupMenu : : set_item_as_separator ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_as_checkable " , " index " , " enable " ) , & PopupMenu : : set_item_as_checkable ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_as_radio_checkable " , " index " , " enable " ) , & PopupMenu : : set_item_as_radio_checkable ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_tooltip " , " index " , " tooltip " ) , & PopupMenu : : set_item_tooltip ) ;
ClassDB : : bind_method ( D_METHOD ( " set_item_shortcut " , " index " , " shortcut " , " global " ) , & PopupMenu : : set_item_shortcut , DEFVAL ( false ) ) ;
2022-08-01 09:28:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_indent " , " index " , " indent " ) , & PopupMenu : : set_item_indent ) ;
2021-12-08 12:02:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_multistate " , " index " , " state " ) , & PopupMenu : : set_item_multistate ) ;
2024-01-20 06:10:28 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_multistate_max " , " index " , " max_states " ) , & PopupMenu : : set_item_max_states ) ;
2021-12-08 12:02:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_shortcut_disabled " , " index " , " disabled " ) , & PopupMenu : : set_item_shortcut_disabled ) ;
ClassDB : : bind_method ( D_METHOD ( " toggle_item_checked " , " index " ) , & PopupMenu : : toggle_item_checked ) ;
ClassDB : : bind_method ( D_METHOD ( " toggle_item_multistate " , " index " ) , & PopupMenu : : toggle_item_multistate ) ;
ClassDB : : bind_method ( D_METHOD ( " get_item_text " , " index " ) , & PopupMenu : : get_item_text ) ;
ClassDB : : bind_method ( D_METHOD ( " get_item_text_direction " , " index " ) , & PopupMenu : : get_item_text_direction ) ;
ClassDB : : bind_method ( D_METHOD ( " get_item_language " , " index " ) , & PopupMenu : : get_item_language ) ;
ClassDB : : bind_method ( D_METHOD ( " get_item_icon " , " index " ) , & PopupMenu : : get_item_icon ) ;
2023-03-31 19:17:59 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_item_icon_max_width " , " index " ) , & PopupMenu : : get_item_icon_max_width ) ;
2022-12-19 05:38:08 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_item_icon_modulate " , " index " ) , & PopupMenu : : get_item_icon_modulate ) ;
2021-12-08 12:02:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_item_checked " , " index " ) , & PopupMenu : : is_item_checked ) ;
ClassDB : : bind_method ( D_METHOD ( " get_item_id " , " index " ) , & PopupMenu : : get_item_id ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_item_index " , " id " ) , & PopupMenu : : get_item_index ) ;
2021-12-08 12:02:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_item_accelerator " , " index " ) , & PopupMenu : : get_item_accelerator ) ;
ClassDB : : bind_method ( D_METHOD ( " get_item_metadata " , " index " ) , & PopupMenu : : get_item_metadata ) ;
ClassDB : : bind_method ( D_METHOD ( " is_item_disabled " , " index " ) , & PopupMenu : : is_item_disabled ) ;
ClassDB : : bind_method ( D_METHOD ( " get_item_submenu " , " index " ) , & PopupMenu : : get_item_submenu ) ;
2023-11-28 16:33:04 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_item_submenu_node " , " index " ) , & PopupMenu : : get_item_submenu_node ) ;
2021-12-08 12:02:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_item_separator " , " index " ) , & PopupMenu : : is_item_separator ) ;
ClassDB : : bind_method ( D_METHOD ( " is_item_checkable " , " index " ) , & PopupMenu : : is_item_checkable ) ;
ClassDB : : bind_method ( D_METHOD ( " is_item_radio_checkable " , " index " ) , & PopupMenu : : is_item_radio_checkable ) ;
ClassDB : : bind_method ( D_METHOD ( " is_item_shortcut_disabled " , " index " ) , & PopupMenu : : is_item_shortcut_disabled ) ;
ClassDB : : bind_method ( D_METHOD ( " get_item_tooltip " , " index " ) , & PopupMenu : : get_item_tooltip ) ;
ClassDB : : bind_method ( D_METHOD ( " get_item_shortcut " , " index " ) , & PopupMenu : : get_item_shortcut ) ;
2022-08-01 09:28:16 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_item_indent " , " index " ) , & PopupMenu : : get_item_indent ) ;
2017-03-05 15:44:50 +00:00
2024-01-20 06:10:28 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_item_multistate_max " , " index " ) , & PopupMenu : : get_item_max_states ) ;
ClassDB : : bind_method ( D_METHOD ( " get_item_multistate " , " index " ) , & PopupMenu : : get_item_state ) ;
2022-09-06 13:51:14 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_focused_item " , " index " ) , & PopupMenu : : set_focused_item ) ;
ClassDB : : bind_method ( D_METHOD ( " get_focused_item " ) , & PopupMenu : : get_focused_item ) ;
2021-11-04 14:27:23 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_item_count " , " count " ) , & PopupMenu : : set_item_count ) ;
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " get_item_count " ) , & PopupMenu : : get_item_count ) ;
2017-03-05 15:44:50 +00:00
2022-02-06 02:47:00 +00:00
ClassDB : : bind_method ( D_METHOD ( " scroll_to_item " , " index " ) , & PopupMenu : : scroll_to_item ) ;
2021-12-08 12:02:42 +00:00
ClassDB : : bind_method ( D_METHOD ( " remove_item " , " index " ) , & PopupMenu : : remove_item ) ;
2017-03-05 15:44:50 +00:00
2020-12-27 12:18:47 +00:00
ClassDB : : bind_method ( D_METHOD ( " add_separator " , " label " , " id " ) , & PopupMenu : : add_separator , DEFVAL ( String ( ) ) , DEFVAL ( - 1 ) ) ;
2023-07-27 17:50:31 +00:00
ClassDB : : bind_method ( D_METHOD ( " clear " , " free_submenus " ) , & PopupMenu : : clear , DEFVAL ( false ) ) ;
2017-03-05 15:44:50 +00:00
2017-02-13 11:47:24 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_hide_on_item_selection " , " enable " ) , & PopupMenu : : set_hide_on_item_selection ) ;
ClassDB : : bind_method ( D_METHOD ( " is_hide_on_item_selection " ) , & PopupMenu : : is_hide_on_item_selection ) ;
2017-03-05 15:44:50 +00:00
2017-04-14 11:49:00 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_hide_on_checkable_item_selection " , " enable " ) , & PopupMenu : : set_hide_on_checkable_item_selection ) ;
ClassDB : : bind_method ( D_METHOD ( " is_hide_on_checkable_item_selection " ) , & PopupMenu : : is_hide_on_checkable_item_selection ) ;
2017-12-12 02:37:37 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_hide_on_state_item_selection " , " enable " ) , & PopupMenu : : set_hide_on_multistate_item_selection ) ;
ClassDB : : bind_method ( D_METHOD ( " is_hide_on_state_item_selection " ) , & PopupMenu : : is_hide_on_multistate_item_selection ) ;
2017-11-22 21:29:27 +00:00
2018-07-09 12:16:06 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_submenu_popup_delay " , " seconds " ) , & PopupMenu : : set_submenu_popup_delay ) ;
ClassDB : : bind_method ( D_METHOD ( " get_submenu_popup_delay " ) , & PopupMenu : : get_submenu_popup_delay ) ;
2018-01-02 07:10:49 +00:00
2019-05-11 16:32:53 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_allow_search " , " allow " ) , & PopupMenu : : set_allow_search ) ;
ClassDB : : bind_method ( D_METHOD ( " get_allow_search " ) , & PopupMenu : : get_allow_search ) ;
2023-10-15 17:52:56 +00:00
ClassDB : : bind_method ( D_METHOD ( " is_system_menu " ) , & PopupMenu : : is_system_menu ) ;
2024-01-19 17:41:01 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_system_menu " , " system_menu_id " ) , & PopupMenu : : set_system_menu ) ;
ClassDB : : bind_method ( D_METHOD ( " get_system_menu " ) , & PopupMenu : : get_system_menu ) ;
2023-10-15 17:52:56 +00:00
2018-11-08 14:30:02 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " hide_on_item_selection " ) , " set_hide_on_item_selection " , " is_hide_on_item_selection " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " hide_on_checkable_item_selection " ) , " set_hide_on_checkable_item_selection " , " is_hide_on_checkable_item_selection " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " hide_on_state_item_selection " ) , " set_hide_on_state_item_selection " , " is_hide_on_state_item_selection " ) ;
2022-05-20 05:24:41 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : FLOAT , " submenu_popup_delay " , PROPERTY_HINT_NONE , " suffix:s " ) , " set_submenu_popup_delay " , " get_submenu_popup_delay " ) ;
2019-05-11 16:32:53 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " allow_search " ) , " set_allow_search " , " get_allow_search " ) ;
2024-03-07 18:42:24 +00:00
ADD_PROPERTY ( PropertyInfo ( Variant : : INT , " system_menu_id " , PROPERTY_HINT_ENUM , " None:0,Application Menu:2,Window Menu:3,Help Menu:4,Dock:5 " ) , " set_system_menu " , " get_system_menu " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " prefer_native_menu " ) , " set_prefer_native_menu " , " is_prefer_native_menu " ) ;
2017-03-05 15:44:50 +00:00
2021-12-07 03:09:31 +00:00
ADD_ARRAY_COUNT ( " Items " , " item_count " , " set_item_count " , " get_item_count " , " item_ " ) ;
2021-11-03 02:08:58 +00:00
2019-05-09 09:21:49 +00:00
ADD_SIGNAL ( MethodInfo ( " id_pressed " , PropertyInfo ( Variant : : INT , " id " ) ) ) ;
ADD_SIGNAL ( MethodInfo ( " id_focused " , PropertyInfo ( Variant : : INT , " id " ) ) ) ;
2017-01-08 21:18:54 +00:00
ADD_SIGNAL ( MethodInfo ( " index_pressed " , PropertyInfo ( Variant : : INT , " index " ) ) ) ;
2022-08-01 09:28:16 +00:00
ADD_SIGNAL ( MethodInfo ( " menu_changed " ) ) ;
2023-09-08 19:00:10 +00:00
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_STYLEBOX , PopupMenu , panel_style , " panel " ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_STYLEBOX , PopupMenu , hover_style , " hover " ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_STYLEBOX , PopupMenu , separator_style , " separator " ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_STYLEBOX , PopupMenu , labeled_separator_left ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_STYLEBOX , PopupMenu , labeled_separator_right ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , PopupMenu , v_separation ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , PopupMenu , h_separation ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , PopupMenu , indent ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , PopupMenu , item_start_padding ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , PopupMenu , item_end_padding ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_CONSTANT , PopupMenu , icon_max_width ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , PopupMenu , checked ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , PopupMenu , checked_disabled ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , PopupMenu , unchecked ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , PopupMenu , unchecked_disabled ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , PopupMenu , radio_checked ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , PopupMenu , radio_checked_disabled ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , PopupMenu , radio_unchecked ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , PopupMenu , radio_unchecked_disabled ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , PopupMenu , submenu ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_ICON , PopupMenu , submenu_mirrored ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_FONT , PopupMenu , font ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_FONT_SIZE , PopupMenu , font_size ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_FONT , PopupMenu , font_separator ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_FONT_SIZE , PopupMenu , font_separator_size ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , PopupMenu , font_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , PopupMenu , font_hover_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , PopupMenu , font_disabled_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , PopupMenu , font_accelerator_color ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_CONSTANT , PopupMenu , font_outline_size , " outline_size " ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , PopupMenu , font_outline_color ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , PopupMenu , font_separator_color ) ;
BIND_THEME_ITEM_CUSTOM ( Theme : : DATA_TYPE_CONSTANT , PopupMenu , font_separator_outline_size , " separator_outline_size " ) ;
BIND_THEME_ITEM ( Theme : : DATA_TYPE_COLOR , PopupMenu , font_separator_outline_color ) ;
2024-02-11 11:09:55 +00:00
Item defaults ( true ) ;
base_property_helper . set_prefix ( " item_ " ) ;
base_property_helper . register_property ( PropertyInfo ( Variant : : STRING , " text " ) , defaults . text , & PopupMenu : : set_item_text , & PopupMenu : : get_item_text ) ;
base_property_helper . register_property ( PropertyInfo ( Variant : : OBJECT , " icon " , PROPERTY_HINT_RESOURCE_TYPE , " Texture2D " ) , defaults . icon , & PopupMenu : : set_item_icon , & PopupMenu : : get_item_icon ) ;
base_property_helper . register_property ( PropertyInfo ( Variant : : INT , " checkable " , PROPERTY_HINT_ENUM , " No,As checkbox,As radio button " ) , defaults . checkable_type , & PopupMenu : : _set_item_checkable_type , & PopupMenu : : _get_item_checkable_type ) ;
base_property_helper . register_property ( PropertyInfo ( Variant : : BOOL , " checked " ) , defaults . checked , & PopupMenu : : set_item_checked , & PopupMenu : : is_item_checked ) ;
base_property_helper . register_property ( PropertyInfo ( Variant : : INT , " id " , PROPERTY_HINT_RANGE , " 0,10,1,or_greater " ) , defaults . id , & PopupMenu : : set_item_id , & PopupMenu : : get_item_id ) ;
base_property_helper . register_property ( PropertyInfo ( Variant : : BOOL , " disabled " ) , defaults . disabled , & PopupMenu : : set_item_disabled , & PopupMenu : : is_item_disabled ) ;
base_property_helper . register_property ( PropertyInfo ( Variant : : BOOL , " separator " ) , defaults . separator , & PopupMenu : : set_item_as_separator , & PopupMenu : : is_item_separator ) ;
2014-02-10 01:10:30 +00:00
}
2016-03-08 23:00:52 +00:00
2021-03-30 12:42:50 +00:00
void PopupMenu : : popup ( const Rect2i & p_bounds ) {
2024-03-07 18:42:24 +00:00
bool native = global_menu . is_valid ( ) ;
# ifdef TOOLS_ENABLED
if ( is_part_of_edited_scene ( ) ) {
native = false ;
}
# endif
if ( native ) {
NativeMenu : : get_singleton ( ) - > popup ( global_menu , ( p_bounds ! = Rect2i ( ) ) ? p_bounds . position : get_position ( ) ) ;
} else {
moved = Vector2 ( ) ;
popup_time_msec = OS : : get_singleton ( ) - > get_ticks_msec ( ) ;
2023-12-27 16:19:52 +00:00
if ( ! is_embedded ( ) ) {
float win_scale = get_parent_visible_window ( ) - > get_content_scale_factor ( ) ;
set_content_scale_factor ( win_scale ) ;
2024-04-24 15:42:50 +00:00
Size2 minsize = get_contents_minimum_size ( ) * win_scale ;
minsize . height = Math : : ceil ( minsize . height ) ; // Ensures enough height at fractional content scales to prevent the v_scroll_bar from showing.
set_min_size ( minsize ) ; // `height` is truncated here by the cast to Size2i for Window.min_size.
2023-12-27 16:19:52 +00:00
set_size ( Vector2 ( 0 , 0 ) ) ; // Shrinkwraps to min size.
}
2024-03-07 18:42:24 +00:00
Popup : : popup ( p_bounds ) ;
}
}
void PopupMenu : : set_visible ( bool p_visible ) {
bool native = global_menu . is_valid ( ) ;
# ifdef TOOLS_ENABLED
if ( is_part_of_edited_scene ( ) ) {
native = false ;
}
# endif
if ( native ) {
if ( p_visible ) {
NativeMenu : : get_singleton ( ) - > popup ( global_menu , get_position ( ) ) ;
}
} else {
Popup : : set_visible ( p_visible ) ;
}
2015-01-03 19:52:37 +00:00
}
2014-02-10 01:10:30 +00:00
PopupMenu : : PopupMenu ( ) {
2020-08-29 12:05:59 +00:00
// Scroll Container
scroll_container = memnew ( ScrollContainer ) ;
2024-02-23 13:51:47 +00:00
scroll_container - > set_anchors_and_offsets_preset ( Control : : PRESET_FULL_RECT ) ;
2020-08-29 12:05:59 +00:00
scroll_container - > set_clip_contents ( true ) ;
2024-02-23 13:51:47 +00:00
add_child ( scroll_container , false , INTERNAL_MODE_FRONT ) ;
2020-08-29 12:05:59 +00:00
// The control which will display the items
2020-03-12 12:37:40 +00:00
control = memnew ( Control ) ;
2020-08-29 12:05:59 +00:00
control - > set_clip_contents ( false ) ;
2022-03-19 00:02:57 +00:00
control - > set_anchors_and_offsets_preset ( Control : : PRESET_FULL_RECT ) ;
2020-08-29 12:05:59 +00:00
control - > set_h_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
control - > set_v_size_flags ( Control : : SIZE_EXPAND_FILL ) ;
2021-08-25 13:49:30 +00:00
scroll_container - > add_child ( control , false , INTERNAL_MODE_FRONT ) ;
2020-08-29 12:05:59 +00:00
control - > connect ( " draw " , callable_mp ( this , & PopupMenu : : _draw_items ) ) ;
2014-02-10 01:10:30 +00:00
submenu_timer = memnew ( Timer ) ;
submenu_timer - > set_wait_time ( 0.3 ) ;
submenu_timer - > set_one_shot ( true ) ;
2020-02-21 17:28:45 +00:00
submenu_timer - > connect ( " timeout " , callable_mp ( this , & PopupMenu : : _submenu_timeout ) ) ;
2021-08-25 13:49:30 +00:00
add_child ( submenu_timer , false , INTERNAL_MODE_FRONT ) ;
2020-09-07 12:57:50 +00:00
minimum_lifetime_timer = memnew ( Timer ) ;
minimum_lifetime_timer - > set_wait_time ( 0.3 ) ;
minimum_lifetime_timer - > set_one_shot ( true ) ;
minimum_lifetime_timer - > connect ( " timeout " , callable_mp ( this , & PopupMenu : : _minimum_lifetime_timeout ) ) ;
2021-08-25 13:49:30 +00:00
add_child ( minimum_lifetime_timer , false , INTERNAL_MODE_FRONT ) ;
2024-02-11 11:09:55 +00:00
property_helper . setup_for_instance ( base_property_helper , this ) ;
2014-02-10 01:10:30 +00:00
}
PopupMenu : : ~ PopupMenu ( ) {
2024-01-19 17:41:01 +00:00
unbind_global_menu ( ) ;
2014-02-10 01:10:30 +00:00
}