| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*  box_container.cpp                                                    */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | /*                       This file is part of:                           */ | 
					
						
							|  |  |  | /*                           GODOT ENGINE                                */ | 
					
						
							|  |  |  | /*                    http://www.godotengine.org                         */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							| 
									
										
										
										
											2017-01-01 22:01:57 +01:00
										 |  |  | /* Copyright (c) 2007-2017 Juan Linietsky, Ariel Manzur.                 */ | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03: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.                */ | 
					
						
							|  |  |  | /*************************************************************************/ | 
					
						
							|  |  |  | #include "box_container.h"
 | 
					
						
							|  |  |  | #include "margin_container.h"
 | 
					
						
							|  |  |  | #include "label.h"
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | struct _MinSizeCache { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int min_size; | 
					
						
							|  |  |  | 	bool will_stretch; | 
					
						
							|  |  |  | 	int final_size; | 
					
						
							|  |  |  | }; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BoxContainer::_resort() { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** First pass, determine minimum size AND amount of stretchable elements */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-14 18:03:38 +01:00
										 |  |  | 	Size2i new_size=get_size(); | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-12-21 11:42:44 -03:00
										 |  |  | 	int sep=get_constant("separation");//,vertical?"VBoxContainer":"HBoxContainer");
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	bool first=true; | 
					
						
							|  |  |  | 	int children_count=0; | 
					
						
							|  |  |  | 	int stretch_min=0; | 
					
						
							|  |  |  | 	int stretch_avail=0; | 
					
						
							|  |  |  | 	float stretch_ratio_total=0; | 
					
						
							|  |  |  | 	Map<Control*,_MinSizeCache> min_size_cache; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for(int i=0;i<get_child_count();i++) { | 
					
						
							|  |  |  | 		Control *c=get_child(i)->cast_to<Control>(); | 
					
						
							| 
									
										
										
										
											2017-01-13 10:45:50 -03:00
										 |  |  | 		if (!c || !c->is_visible_in_tree()) | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 			continue; | 
					
						
							|  |  |  | 		if (c->is_set_as_toplevel()) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Size2i size=c->get_combined_minimum_size(); | 
					
						
							|  |  |  | 		_MinSizeCache msc; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (vertical) { /* VERTICAL */ | 
					
						
							|  |  |  | 			stretch_min+=size.height; | 
					
						
							|  |  |  | 			msc.min_size=size.height; | 
					
						
							|  |  |  | 			msc.will_stretch=c->get_v_size_flags() & SIZE_EXPAND; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else { /* HORIZONTAL */ | 
					
						
							|  |  |  | 			stretch_min+=size.width; | 
					
						
							|  |  |  | 			msc.min_size=size.width; | 
					
						
							|  |  |  | 			msc.will_stretch=c->get_h_size_flags() & SIZE_EXPAND; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (msc.will_stretch) { | 
					
						
							|  |  |  | 			stretch_avail+=msc.min_size; | 
					
						
							|  |  |  | 			stretch_ratio_total+=c->get_stretch_ratio(); | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 		msc.final_size=msc.min_size; | 
					
						
							|  |  |  | 		min_size_cache[c]=msc; | 
					
						
							|  |  |  | 		children_count++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	if (children_count==0) | 
					
						
							|  |  |  | 		return; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int stretch_max =  (vertical? new_size.height : new_size.width ) - (children_count-1) * sep; | 
					
						
							|  |  |  | 	int stretch_diff = stretch_max - stretch_min; | 
					
						
							|  |  |  | 	if (stretch_diff<0) { | 
					
						
							|  |  |  | 		//avoid negative stretch space
 | 
					
						
							|  |  |  | 		stretch_max=stretch_min; | 
					
						
							|  |  |  | 		stretch_diff=0; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	stretch_avail+=stretch_diff; //available stretch space.
 | 
					
						
							|  |  |  | 	/** Second, pass sucessively to discard elements that can't be stretched, this will run while stretchable
 | 
					
						
							|  |  |  | 		elements exist */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-24 14:04:15 +01:00
										 |  |  | 	bool has_stretched = false; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	while(stretch_ratio_total>0) { // first of all, dont even be here if no stretchable objects exist
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-24 14:04:15 +01:00
										 |  |  | 		has_stretched = true; | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 		bool refit_successful=true; //assume refit-test will go well
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		for(int i=0;i<get_child_count();i++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			Control *c=get_child(i)->cast_to<Control>(); | 
					
						
							| 
									
										
										
										
											2017-01-13 10:45:50 -03:00
										 |  |  | 			if (!c || !c->is_visible_in_tree()) | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 				continue; | 
					
						
							|  |  |  | 			if (c->is_set_as_toplevel()) | 
					
						
							|  |  |  | 				continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			ERR_FAIL_COND(!min_size_cache.has(c)); | 
					
						
							|  |  |  | 			_MinSizeCache &msc=min_size_cache[c]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if (msc.will_stretch) { //wants to stretch
 | 
					
						
							|  |  |  | 				//let's see if it can really stretch
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 				int final_pixel_size=stretch_avail * c->get_stretch_ratio() / stretch_ratio_total; | 
					
						
							|  |  |  | 				if (final_pixel_size<msc.min_size) { | 
					
						
							|  |  |  | 					//if available stretching area is too small for widget,
 | 
					
						
							|  |  |  | 					//then remove it from stretching area
 | 
					
						
							|  |  |  | 					msc.will_stretch=false; | 
					
						
							|  |  |  | 					stretch_ratio_total-=c->get_stretch_ratio(); | 
					
						
							|  |  |  | 					refit_successful=false; | 
					
						
							|  |  |  | 					stretch_avail-=msc.min_size; | 
					
						
							|  |  |  | 					msc.final_size=msc.min_size; | 
					
						
							|  |  |  | 					break; | 
					
						
							|  |  |  | 				} else { | 
					
						
							|  |  |  | 					msc.final_size=final_pixel_size; | 
					
						
							|  |  |  | 				} | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (refit_successful) //uf refit went well, break
 | 
					
						
							|  |  |  | 			break; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/** Final pass, draw and stretch elements **/ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	int ofs=0; | 
					
						
							| 
									
										
										
										
											2015-09-24 14:04:15 +01:00
										 |  |  | 	if (!has_stretched) { | 
					
						
							|  |  |  | 		switch (align) { | 
					
						
							|  |  |  | 			case ALIGN_BEGIN: | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			case ALIGN_CENTER: | 
					
						
							|  |  |  | 				ofs = stretch_diff / 2; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 			case ALIGN_END: | 
					
						
							|  |  |  | 				ofs = stretch_diff; | 
					
						
							|  |  |  | 				break; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 	} | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	first=true; | 
					
						
							|  |  |  | 	int idx=0; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for(int i=0;i<get_child_count();i++) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Control *c=get_child(i)->cast_to<Control>(); | 
					
						
							| 
									
										
										
										
											2017-01-13 10:45:50 -03:00
										 |  |  | 		if (!c || !c->is_visible_in_tree()) | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 			continue; | 
					
						
							|  |  |  | 		if (c->is_set_as_toplevel()) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		_MinSizeCache &msc=min_size_cache[c]; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (first) | 
					
						
							|  |  |  | 			first=false; | 
					
						
							|  |  |  | 		else | 
					
						
							|  |  |  | 			ofs+=sep; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		int from=ofs; | 
					
						
							|  |  |  | 		int to=ofs+msc.final_size; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (msc.will_stretch && idx==children_count-1) { | 
					
						
							|  |  |  | 			//adjust so the last one always fits perfect
 | 
					
						
							|  |  |  | 			//compensating for numerical imprecision
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			to=vertical?new_size.height:new_size.width; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		int size=to-from; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Rect2 rect; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (vertical) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			rect=Rect2(0,from,new_size.width,size); | 
					
						
							|  |  |  | 		} else { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			rect=Rect2(from,0,size,new_size.height); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		fit_child_in_rect(c,rect); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		ofs=to; | 
					
						
							|  |  |  | 		idx++; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | Size2 BoxContainer::get_minimum_size() const { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	/* Calculate MINIMUM SIZE */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Size2i minimum; | 
					
						
							| 
									
										
										
										
											2014-12-21 11:42:44 -03:00
										 |  |  | 	int sep=get_constant("separation");//,vertical?"VBoxContainer":"HBoxContainer");
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	bool first=true; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	for(int i=0;i<get_child_count();i++) { | 
					
						
							|  |  |  | 		Control *c=get_child(i)->cast_to<Control>(); | 
					
						
							|  |  |  | 		if (!c) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 		if (c->is_set_as_toplevel()) | 
					
						
							|  |  |  | 			continue; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-13 10:45:50 -03:00
										 |  |  | 		if (!c->is_visible()) { | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 			continue; | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		Size2i size=c->get_combined_minimum_size(); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		if (vertical) { /* VERTICAL */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if ( size.width > minimum.width ) { | 
					
						
							|  |  |  | 				minimum.width=size.width; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			minimum.height+=size.height+(first?0:sep); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} else { /* HORIZONTAL */ | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			if ( size.height > minimum.height ) { | 
					
						
							|  |  |  | 				minimum.height=size.height; | 
					
						
							|  |  |  | 			} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			minimum.width+=size.width+(first?0:sep); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		first=false; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return minimum; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | void BoxContainer::_notification(int p_what) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	switch(p_what) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 		case NOTIFICATION_SORT_CHILDREN: { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 			_resort(); | 
					
						
							|  |  |  | 		} break; | 
					
						
							|  |  |  | 	} | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-24 14:04:15 +01:00
										 |  |  | void BoxContainer::set_alignment(AlignMode p_align) { | 
					
						
							|  |  |  | 	align = p_align; | 
					
						
							|  |  |  | 	_resort(); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BoxContainer::AlignMode BoxContainer::get_alignment() const { | 
					
						
							|  |  |  | 	return align; | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | void BoxContainer::add_spacer(bool p_begin) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Control *c = memnew( Control ); | 
					
						
							| 
									
										
										
										
											2017-01-08 20:58:39 -03:00
										 |  |  | 	c->set_mouse_filter(MOUSE_FILTER_PASS); //allow spacer to pass mouse events
 | 
					
						
							| 
									
										
										
										
											2017-01-08 19:54:19 -03:00
										 |  |  | 
 | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 	if (vertical) | 
					
						
							|  |  |  | 		c->set_v_size_flags(SIZE_EXPAND_FILL); | 
					
						
							|  |  |  | 	else | 
					
						
							|  |  |  | 		c->set_h_size_flags(SIZE_EXPAND_FILL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	add_child(c); | 
					
						
							|  |  |  | 	if (p_begin) | 
					
						
							|  |  |  | 		move_child(c,0); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | BoxContainer::BoxContainer(bool p_vertical) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	vertical=p_vertical; | 
					
						
							| 
									
										
										
										
											2015-09-24 14:04:15 +01:00
										 |  |  | 	align = ALIGN_BEGIN; | 
					
						
							| 
									
										
										
										
											2017-01-14 12:26:56 +01:00
										 |  |  | 	//set_ignore_mouse(true);
 | 
					
						
							| 
									
										
										
										
											2017-01-08 19:54:19 -03:00
										 |  |  | 	set_mouse_filter(MOUSE_FILTER_PASS); | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2015-09-24 14:04:15 +01:00
										 |  |  | void BoxContainer::_bind_methods() { | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-01-02 23:03:46 -03:00
										 |  |  | 	ClassDB::bind_method(_MD("add_spacer","begin"),&BoxContainer::add_spacer); | 
					
						
							|  |  |  | 	ClassDB::bind_method(_MD("get_alignment"),&BoxContainer::get_alignment); | 
					
						
							|  |  |  | 	ClassDB::bind_method(_MD("set_alignment","alignment"),&BoxContainer::set_alignment); | 
					
						
							| 
									
										
										
										
											2015-09-24 14:04:15 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | 	BIND_CONSTANT( ALIGN_BEGIN ); | 
					
						
							|  |  |  | 	BIND_CONSTANT( ALIGN_CENTER ); | 
					
						
							|  |  |  | 	BIND_CONSTANT( ALIGN_END ); | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2017-02-12 01:11:37 +01:00
										 |  |  | 	ADD_PROPERTY( PropertyInfo(Variant::INT,"alignment", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment","get_alignment") ; | 
					
						
							| 
									
										
										
										
											2015-09-24 14:04:15 +01:00
										 |  |  | 
 | 
					
						
							|  |  |  | } | 
					
						
							| 
									
										
										
										
											2014-02-09 22:10:30 -03:00
										 |  |  | 
 | 
					
						
							|  |  |  | MarginContainer* VBoxContainer::add_margin_child(const String& p_label,Control *p_control,bool p_expand) { | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	Label *l = memnew( Label ); | 
					
						
							|  |  |  | 	l->set_text(p_label); | 
					
						
							|  |  |  | 	add_child(l); | 
					
						
							|  |  |  | 	MarginContainer *mc = memnew( MarginContainer ); | 
					
						
							|  |  |  | 	mc->add_child(p_control); | 
					
						
							|  |  |  | 	add_child(mc); | 
					
						
							|  |  |  | 	if (p_expand) | 
					
						
							|  |  |  | 		mc->set_v_size_flags(SIZE_EXPAND_FILL); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | 	return mc; | 
					
						
							|  |  |  | } |