1/**************************************************************************/
2/* aspect_ratio_container.cpp */
3/**************************************************************************/
4/* This file is part of: */
5/* GODOT ENGINE */
6/* https://godotengine.org */
7/**************************************************************************/
8/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
9/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */
10/* */
11/* Permission is hereby granted, free of charge, to any person obtaining */
12/* a copy of this software and associated documentation files (the */
13/* "Software"), to deal in the Software without restriction, including */
14/* without limitation the rights to use, copy, modify, merge, publish, */
15/* distribute, sublicense, and/or sell copies of the Software, and to */
16/* permit persons to whom the Software is furnished to do so, subject to */
17/* the following conditions: */
18/* */
19/* The above copyright notice and this permission notice shall be */
20/* included in all copies or substantial portions of the Software. */
21/* */
22/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */
23/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */
24/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
25/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */
26/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */
27/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */
28/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */
29/**************************************************************************/
30
31#include "aspect_ratio_container.h"
32
33#include "scene/gui/texture_rect.h"
34
35Size2 AspectRatioContainer::get_minimum_size() const {
36 Size2 ms;
37 for (int i = 0; i < get_child_count(); i++) {
38 Control *c = Object::cast_to<Control>(get_child(i));
39 if (!c) {
40 continue;
41 }
42 if (c->is_set_as_top_level()) {
43 continue;
44 }
45 if (!c->is_visible()) {
46 continue;
47 }
48 Size2 minsize = c->get_combined_minimum_size();
49 ms.width = MAX(ms.width, minsize.width);
50 ms.height = MAX(ms.height, minsize.height);
51 }
52 return ms;
53}
54
55void AspectRatioContainer::set_ratio(float p_ratio) {
56 if (ratio == p_ratio) {
57 return;
58 }
59 ratio = p_ratio;
60 queue_sort();
61}
62
63void AspectRatioContainer::set_stretch_mode(StretchMode p_mode) {
64 if (stretch_mode == p_mode) {
65 return;
66 }
67 stretch_mode = p_mode;
68 queue_sort();
69}
70
71void AspectRatioContainer::set_alignment_horizontal(AlignmentMode p_alignment_horizontal) {
72 if (alignment_horizontal == p_alignment_horizontal) {
73 return;
74 }
75 alignment_horizontal = p_alignment_horizontal;
76 queue_sort();
77}
78
79void AspectRatioContainer::set_alignment_vertical(AlignmentMode p_alignment_vertical) {
80 if (alignment_vertical == p_alignment_vertical) {
81 return;
82 }
83 alignment_vertical = p_alignment_vertical;
84 queue_sort();
85}
86
87Vector<int> AspectRatioContainer::get_allowed_size_flags_horizontal() const {
88 Vector<int> flags;
89 flags.append(SIZE_FILL);
90 flags.append(SIZE_SHRINK_BEGIN);
91 flags.append(SIZE_SHRINK_CENTER);
92 flags.append(SIZE_SHRINK_END);
93 return flags;
94}
95
96Vector<int> AspectRatioContainer::get_allowed_size_flags_vertical() const {
97 Vector<int> flags;
98 flags.append(SIZE_FILL);
99 flags.append(SIZE_SHRINK_BEGIN);
100 flags.append(SIZE_SHRINK_CENTER);
101 flags.append(SIZE_SHRINK_END);
102 return flags;
103}
104
105void AspectRatioContainer::_notification(int p_what) {
106 switch (p_what) {
107 case NOTIFICATION_SORT_CHILDREN: {
108 bool rtl = is_layout_rtl();
109 Size2 size = get_size();
110 for (int i = 0; i < get_child_count(); i++) {
111 Control *c = Object::cast_to<Control>(get_child(i));
112 if (!c) {
113 continue;
114 }
115 if (c->is_set_as_top_level()) {
116 continue;
117 }
118
119 // Temporary fix for editor crash.
120 TextureRect *trect = Object::cast_to<TextureRect>(c);
121 if (trect) {
122 if (trect->get_expand_mode() == TextureRect::EXPAND_FIT_WIDTH_PROPORTIONAL || trect->get_expand_mode() == TextureRect::EXPAND_FIT_HEIGHT_PROPORTIONAL) {
123 WARN_PRINT_ONCE("Proportional TextureRect is currently not supported inside AspectRatioContainer");
124 continue;
125 }
126 }
127
128 Size2 child_minsize = c->get_combined_minimum_size();
129 Size2 child_size = Size2(ratio, 1.0);
130 float scale_factor = 1.0;
131
132 switch (stretch_mode) {
133 case STRETCH_WIDTH_CONTROLS_HEIGHT: {
134 scale_factor = size.x / child_size.x;
135 } break;
136 case STRETCH_HEIGHT_CONTROLS_WIDTH: {
137 scale_factor = size.y / child_size.y;
138 } break;
139 case STRETCH_FIT: {
140 scale_factor = MIN(size.x / child_size.x, size.y / child_size.y);
141 } break;
142 case STRETCH_COVER: {
143 scale_factor = MAX(size.x / child_size.x, size.y / child_size.y);
144 } break;
145 }
146 child_size *= scale_factor;
147 child_size.x = MAX(child_size.x, child_minsize.x);
148 child_size.y = MAX(child_size.y, child_minsize.y);
149
150 float align_x = 0.5;
151 switch (alignment_horizontal) {
152 case ALIGNMENT_BEGIN: {
153 align_x = 0.0;
154 } break;
155 case ALIGNMENT_CENTER: {
156 align_x = 0.5;
157 } break;
158 case ALIGNMENT_END: {
159 align_x = 1.0;
160 } break;
161 }
162 float align_y = 0.5;
163 switch (alignment_vertical) {
164 case ALIGNMENT_BEGIN: {
165 align_y = 0.0;
166 } break;
167 case ALIGNMENT_CENTER: {
168 align_y = 0.5;
169 } break;
170 case ALIGNMENT_END: {
171 align_y = 1.0;
172 } break;
173 }
174 Vector2 offset = (size - child_size) * Vector2(align_x, align_y);
175
176 if (rtl) {
177 fit_child_in_rect(c, Rect2(Vector2(size.x - offset.x - child_size.x, offset.y), child_size));
178 } else {
179 fit_child_in_rect(c, Rect2(offset, child_size));
180 }
181 }
182 } break;
183 }
184}
185
186void AspectRatioContainer::_bind_methods() {
187 ClassDB::bind_method(D_METHOD("set_ratio", "ratio"), &AspectRatioContainer::set_ratio);
188 ClassDB::bind_method(D_METHOD("get_ratio"), &AspectRatioContainer::get_ratio);
189
190 ClassDB::bind_method(D_METHOD("set_stretch_mode", "stretch_mode"), &AspectRatioContainer::set_stretch_mode);
191 ClassDB::bind_method(D_METHOD("get_stretch_mode"), &AspectRatioContainer::get_stretch_mode);
192
193 ClassDB::bind_method(D_METHOD("set_alignment_horizontal", "alignment_horizontal"), &AspectRatioContainer::set_alignment_horizontal);
194 ClassDB::bind_method(D_METHOD("get_alignment_horizontal"), &AspectRatioContainer::get_alignment_horizontal);
195
196 ClassDB::bind_method(D_METHOD("set_alignment_vertical", "alignment_vertical"), &AspectRatioContainer::set_alignment_vertical);
197 ClassDB::bind_method(D_METHOD("get_alignment_vertical"), &AspectRatioContainer::get_alignment_vertical);
198
199 ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "ratio", PROPERTY_HINT_RANGE, "0.001,10.0,0.0001,or_greater"), "set_ratio", "get_ratio");
200 ADD_PROPERTY(PropertyInfo(Variant::INT, "stretch_mode", PROPERTY_HINT_ENUM, "Width Controls Height,Height Controls Width,Fit,Cover"), "set_stretch_mode", "get_stretch_mode");
201
202 ADD_GROUP("Alignment", "alignment_");
203 ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment_horizontal", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment_horizontal", "get_alignment_horizontal");
204 ADD_PROPERTY(PropertyInfo(Variant::INT, "alignment_vertical", PROPERTY_HINT_ENUM, "Begin,Center,End"), "set_alignment_vertical", "get_alignment_vertical");
205
206 BIND_ENUM_CONSTANT(STRETCH_WIDTH_CONTROLS_HEIGHT);
207 BIND_ENUM_CONSTANT(STRETCH_HEIGHT_CONTROLS_WIDTH);
208 BIND_ENUM_CONSTANT(STRETCH_FIT);
209 BIND_ENUM_CONSTANT(STRETCH_COVER);
210
211 BIND_ENUM_CONSTANT(ALIGNMENT_BEGIN);
212 BIND_ENUM_CONSTANT(ALIGNMENT_CENTER);
213 BIND_ENUM_CONSTANT(ALIGNMENT_END);
214}
215