2014-02-10 01:10:30 +00:00
/*************************************************************************/
/* popup.cpp */
/*************************************************************************/
/* This file is part of: */
/* GODOT ENGINE */
2017-08-27 12:16:55 +00:00
/* https://godotengine.org */
2014-02-10 01:10:30 +00:00
/*************************************************************************/
2022-01-13 08:45:09 +00:00
/* Copyright (c) 2007-2022 Juan Linietsky, Ariel Manzur. */
/* Copyright (c) 2014-2022 Godot Engine contributors (cf. AUTHORS.md). */
2014-02-10 01:10:30 +00:00
/* */
/* 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.h"
2017-08-18 23:02:56 +00:00
2018-09-11 16:13:45 +00:00
# include "core/engine.h"
# include "core/os/keyboard.h"
2014-02-10 01:10:30 +00:00
2017-05-20 15:38:03 +00:00
void Popup : : _gui_input ( Ref < InputEvent > p_event ) {
2014-02-10 01:10:30 +00:00
}
void Popup : : _notification ( int p_what ) {
2017-03-05 15:44:50 +00:00
if ( p_what = = NOTIFICATION_VISIBILITY_CHANGED ) {
2017-01-13 13:45:50 +00:00
if ( popped_up & & ! is_visible_in_tree ( ) ) {
2017-03-05 15:44:50 +00:00
popped_up = false ;
2014-05-05 01:50:23 +00:00
notification ( NOTIFICATION_POPUP_HIDE ) ;
emit_signal ( " popup_hide " ) ;
}
2016-05-17 21:27:15 +00:00
update_configuration_warning ( ) ;
2014-05-05 01:50:23 +00:00
}
2015-12-29 12:26:17 +00:00
2019-07-06 14:37:40 +00:00
if ( p_what = = NOTIFICATION_EXIT_TREE ) {
if ( popped_up ) {
popped_up = false ;
notification ( NOTIFICATION_POPUP_HIDE ) ;
emit_signal ( " popup_hide " ) ;
}
}
2017-03-05 15:44:50 +00:00
if ( p_what = = NOTIFICATION_ENTER_TREE ) {
//small helper to make editing of these easier in editor
2015-12-29 15:14:02 +00:00
# ifdef TOOLS_ENABLED
2017-08-18 23:02:56 +00:00
if ( Engine : : get_singleton ( ) - > is_editor_hint ( ) & & get_tree ( ) - > get_edited_scene_root ( ) & & get_tree ( ) - > get_edited_scene_root ( ) - > is_a_parent_of ( this ) ) {
2019-01-14 16:16:19 +00:00
//edited on editor
2015-12-29 12:26:17 +00:00
set_as_toplevel ( false ) ;
2019-01-14 16:16:19 +00:00
} else
2015-12-29 15:14:02 +00:00
# endif
2019-01-14 16:16:19 +00:00
if ( is_visible ( ) ) {
hide ( ) ;
}
2015-12-29 12:26:17 +00:00
}
2014-02-10 01:10:30 +00:00
}
void Popup : : _fix_size ( ) {
2017-03-29 15:29:38 +00:00
Point2 pos = get_global_position ( ) ;
2019-04-23 01:28:38 +00:00
Size2 size = get_size ( ) * get_scale ( ) ;
2019-08-10 12:41:16 +00:00
Point2 window_size = get_viewport_rect ( ) . size - get_viewport_transform ( ) . get_origin ( ) ;
2014-02-10 01:10:30 +00:00
2021-05-05 10:44:11 +00:00
if ( pos . x + size . width > window_size . width ) {
2017-03-05 15:44:50 +00:00
pos . x = window_size . width - size . width ;
2021-05-05 10:44:11 +00:00
}
if ( pos . x < 0 ) {
2017-03-05 15:44:50 +00:00
pos . x = 0 ;
2021-05-05 10:44:11 +00:00
}
2017-03-05 15:44:50 +00:00
2021-05-05 10:44:11 +00:00
if ( pos . y + size . height > window_size . height ) {
2017-03-05 15:44:50 +00:00
pos . y = window_size . height - size . height ;
2021-05-05 10:44:11 +00:00
}
if ( pos . y < 0 ) {
2017-03-05 15:44:50 +00:00
pos . y = 0 ;
2021-05-05 10:44:11 +00:00
}
if ( pos ! = get_position ( ) ) {
2017-03-29 15:29:38 +00:00
set_global_position ( pos ) ;
2021-05-05 10:44:11 +00:00
}
2014-02-10 01:10:30 +00:00
}
2015-06-14 01:12:53 +00:00
void Popup : : set_as_minsize ( ) {
Size2 total_minsize ;
2017-03-05 15:44:50 +00:00
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
2017-08-24 20:58:51 +00:00
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2021-05-05 10:44:11 +00:00
if ( ! c ) {
2015-06-14 01:12:53 +00:00
continue ;
2021-05-05 10:44:11 +00:00
}
if ( ! c - > is_visible ( ) ) {
2015-06-14 01:12:53 +00:00
continue ;
2021-05-05 10:44:11 +00:00
}
2015-06-14 01:12:53 +00:00
Size2 minsize = c - > get_combined_minimum_size ( ) ;
2017-03-05 15:44:50 +00:00
for ( int j = 0 ; j < 2 ; j + + ) {
Margin m_beg = Margin ( 0 + j ) ;
Margin m_end = Margin ( 2 + j ) ;
2015-06-14 01:12:53 +00:00
float margin_begin = c - > get_margin ( m_beg ) ;
float margin_end = c - > get_margin ( m_end ) ;
2017-07-06 07:16:27 +00:00
float anchor_begin = c - > get_anchor ( m_beg ) ;
float anchor_end = c - > get_anchor ( m_end ) ;
2015-06-14 01:12:53 +00:00
2017-07-06 07:16:27 +00:00
minsize [ j ] + = margin_begin * ( ANCHOR_END - anchor_begin ) + margin_end * anchor_end ;
2015-06-14 01:12:53 +00:00
}
2017-03-05 15:44:50 +00:00
total_minsize . width = MAX ( total_minsize . width , minsize . width ) ;
total_minsize . height = MAX ( total_minsize . height , minsize . height ) ;
2015-06-14 01:12:53 +00:00
}
set_size ( total_minsize ) ;
}
2019-04-26 19:36:44 +00:00
void Popup : : popup_centered_clamped ( const Size2 & p_size , float p_fallback_ratio ) {
Size2 popup_size = p_size ;
Size2 window_size = get_viewport_rect ( ) . size ;
// clamp popup size in each dimension if window size is too small (using fallback ratio)
popup_size . x = MIN ( window_size . x * p_fallback_ratio , popup_size . x ) ;
popup_size . y = MIN ( window_size . y * p_fallback_ratio , popup_size . y ) ;
popup_centered ( popup_size ) ;
}
2017-03-05 15:44:50 +00:00
void Popup : : popup_centered_minsize ( const Size2 & p_minsize ) {
2018-06-02 00:28:49 +00:00
set_custom_minimum_size ( p_minsize ) ;
_fix_size ( ) ;
popup_centered ( ) ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
void Popup : : popup_centered ( const Size2 & p_size ) {
2014-02-10 01:10:30 +00:00
Rect2 rect ;
2019-02-13 14:51:38 +00:00
Size2 window_size = get_viewport_rect ( ) . size ;
2017-03-05 15:44:50 +00:00
rect . size = p_size = = Size2 ( ) ? get_size ( ) : p_size ;
2021-01-27 17:12:09 +00:00
rect . position = ( ( window_size - rect . size * get_scale ( ) ) / 2.0 ) . floor ( ) ;
2014-02-10 01:10:30 +00:00
2019-06-25 00:37:32 +00:00
_popup ( rect , true ) ;
2014-02-10 01:10:30 +00:00
}
void Popup : : popup_centered_ratio ( float p_screen_ratio ) {
Rect2 rect ;
2019-02-13 14:51:38 +00:00
Size2 window_size = get_viewport_rect ( ) . size ;
2014-02-10 01:10:30 +00:00
rect . size = ( window_size * p_screen_ratio ) . floor ( ) ;
2021-01-27 17:12:09 +00:00
rect . position = ( ( window_size - rect . size * get_scale ( ) ) / 2.0 ) . floor ( ) ;
2016-03-08 23:00:52 +00:00
2019-06-25 00:37:32 +00:00
_popup ( rect , true ) ;
2014-02-10 01:10:30 +00:00
}
2017-08-11 19:10:05 +00:00
void Popup : : popup ( const Rect2 & p_bounds ) {
2019-06-25 00:37:32 +00:00
_popup ( p_bounds ) ;
}
void Popup : : _popup ( const Rect2 & p_bounds , const bool p_centered ) {
2014-02-10 01:10:30 +00:00
emit_signal ( " about_to_show " ) ;
show_modal ( exclusive ) ;
2017-03-02 21:43:56 +00:00
// Fit the popup into the optionally provided bounds.
2017-08-11 19:10:05 +00:00
if ( ! p_bounds . has_no_area ( ) ) {
set_size ( p_bounds . size ) ;
2019-06-25 00:37:32 +00:00
// check if p_bounds.size was using an outdated cached values
if ( p_centered & & p_bounds . size ! = get_size ( ) ) {
set_position ( p_bounds . position - ( ( get_size ( ) - p_bounds . size ) / 2.0 ) . floor ( ) ) ;
} else {
set_position ( p_bounds . position ) ;
}
2017-03-02 21:43:56 +00:00
}
2014-02-10 01:10:30 +00:00
_fix_size ( ) ;
Control * focusable = find_next_valid_focus ( ) ;
2021-05-05 10:44:11 +00:00
if ( focusable ) {
2014-02-10 01:10:30 +00:00
focusable - > grab_focus ( ) ;
2021-05-05 10:44:11 +00:00
}
2014-02-10 01:10:30 +00:00
_post_popup ( ) ;
notification ( NOTIFICATION_POST_POPUP ) ;
2017-03-05 15:44:50 +00:00
popped_up = true ;
2014-02-10 01:10:30 +00:00
}
void Popup : : set_exclusive ( bool p_exclusive ) {
2017-03-05 15:44:50 +00:00
exclusive = p_exclusive ;
2014-02-10 01:10:30 +00:00
}
2017-03-05 15:44:50 +00:00
bool Popup : : is_exclusive ( ) const {
2014-02-10 01:10:30 +00:00
return exclusive ;
}
void Popup : : _bind_methods ( ) {
2019-08-14 06:35:08 +00:00
ClassDB : : bind_method ( D_METHOD ( " set_as_minsize " ) , & Popup : : set_as_minsize ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " popup_centered " , " size " ) , & Popup : : popup_centered , DEFVAL ( Size2 ( ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " popup_centered_ratio " , " ratio " ) , & Popup : : popup_centered_ratio , DEFVAL ( 0.75 ) ) ;
ClassDB : : bind_method ( D_METHOD ( " popup_centered_minsize " , " minsize " ) , & Popup : : popup_centered_minsize , DEFVAL ( Size2 ( ) ) ) ;
2019-04-26 19:36:44 +00:00
ClassDB : : bind_method ( D_METHOD ( " popup_centered_clamped " , " size " , " fallback_ratio " ) , & Popup : : popup_centered_clamped , DEFVAL ( Size2 ( ) ) , DEFVAL ( 0.75 ) ) ;
2017-03-05 15:44:50 +00:00
ClassDB : : bind_method ( D_METHOD ( " popup " , " bounds " ) , & Popup : : popup , DEFVAL ( Rect2 ( ) ) ) ;
ClassDB : : bind_method ( D_METHOD ( " set_exclusive " , " enable " ) , & Popup : : set_exclusive ) ;
ClassDB : : bind_method ( D_METHOD ( " is_exclusive " ) , & Popup : : is_exclusive ) ;
ADD_SIGNAL ( MethodInfo ( " about_to_show " ) ) ;
ADD_SIGNAL ( MethodInfo ( " popup_hide " ) ) ;
ADD_GROUP ( " Popup " , " popup_ " ) ;
ADD_PROPERTY ( PropertyInfo ( Variant : : BOOL , " popup_exclusive " ) , " set_exclusive " , " is_exclusive " ) ;
2017-08-20 15:45:01 +00:00
2014-02-10 01:10:30 +00:00
BIND_CONSTANT ( NOTIFICATION_POST_POPUP ) ;
2014-05-05 01:50:23 +00:00
BIND_CONSTANT ( NOTIFICATION_POPUP_HIDE ) ;
2014-02-10 01:10:30 +00:00
}
Popup : : Popup ( ) {
set_as_toplevel ( true ) ;
2017-03-05 15:44:50 +00:00
exclusive = false ;
popped_up = false ;
2014-02-10 01:10:30 +00:00
hide ( ) ;
}
2016-05-17 21:27:15 +00:00
String Popup : : get_configuration_warning ( ) const {
2020-03-22 08:31:09 +00:00
String warning = Control : : get_configuration_warning ( ) ;
2017-01-13 13:45:50 +00:00
if ( is_visible_in_tree ( ) ) {
2020-03-22 08:31:09 +00:00
if ( warning ! = String ( ) ) {
warning + = " \n \n " ;
}
warning + = TTR ( " Popups will hide by default unless you call popup() or any of the popup*() functions. Making them visible for editing is fine, but they will hide upon running. " ) ;
2016-05-17 21:27:15 +00:00
}
2020-03-22 08:31:09 +00:00
return warning ;
2016-05-17 21:27:15 +00:00
}
2014-02-10 01:10:30 +00:00
2017-03-05 15:44:50 +00:00
Popup : : ~ Popup ( ) {
2014-02-10 01:10:30 +00:00
}
2018-09-07 16:49:10 +00:00
Size2 PopupPanel : : get_minimum_size ( ) const {
2014-02-10 01:10:30 +00:00
Ref < StyleBox > p = get_stylebox ( " panel " ) ;
2018-09-07 16:49:10 +00:00
Size2 ms ;
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2021-05-05 10:44:11 +00:00
if ( ! c ) {
2018-09-07 16:49:10 +00:00
continue ;
2021-05-05 10:44:11 +00:00
}
2018-09-07 16:49:10 +00:00
2021-05-05 10:44:11 +00:00
if ( c - > is_set_as_toplevel ( ) ) {
2018-09-07 16:49:10 +00:00
continue ;
2021-05-05 10:44:11 +00:00
}
2018-09-07 16:49:10 +00:00
Size2 cms = c - > get_combined_minimum_size ( ) ;
ms . x = MAX ( cms . x , ms . x ) ;
ms . y = MAX ( cms . y , ms . y ) ;
}
return ms + p - > get_minimum_size ( ) ;
}
void PopupPanel : : _update_child_rects ( ) {
Ref < StyleBox > p = get_stylebox ( " panel " ) ;
Vector2 cpos ( p - > get_offset ( ) ) ;
Vector2 csize ( get_size ( ) - p - > get_minimum_size ( ) ) ;
for ( int i = 0 ; i < get_child_count ( ) ; i + + ) {
Control * c = Object : : cast_to < Control > ( get_child ( i ) ) ;
2021-05-05 10:44:11 +00:00
if ( ! c ) {
2018-09-07 16:49:10 +00:00
continue ;
2021-05-05 10:44:11 +00:00
}
2018-09-07 16:49:10 +00:00
2021-05-05 10:44:11 +00:00
if ( c - > is_set_as_toplevel ( ) ) {
2018-09-07 16:49:10 +00:00
continue ;
2021-05-05 10:44:11 +00:00
}
2018-09-07 16:49:10 +00:00
c - > set_position ( cpos ) ;
c - > set_size ( csize ) ;
}
2014-02-10 01:10:30 +00:00
}
void PopupPanel : : _notification ( int p_what ) {
2017-03-05 15:44:50 +00:00
if ( p_what = = NOTIFICATION_DRAW ) {
get_stylebox ( " panel " ) - > draw ( get_canvas_item ( ) , Rect2 ( Point2 ( ) , get_size ( ) ) ) ;
2018-09-07 16:49:10 +00:00
} else if ( p_what = = NOTIFICATION_READY ) {
_update_child_rects ( ) ;
} else if ( p_what = = NOTIFICATION_RESIZED ) {
_update_child_rects ( ) ;
2014-02-10 01:10:30 +00:00
}
}
PopupPanel : : PopupPanel ( ) {
}