Add tests for Container nodes

This commit is contained in:
StarryWorm committed 2026-04-05 17:50:14 -04:00
1 parent ee713ccb7c
commit 039d474aa7
9 files changed
+2999

No files matched your search

+358
View File
@@ -0,0 +1,358 @@
/**************************************************************************/
/* test_aspect_ratio_container.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. */
/**************************************************************************/
#include "tests/test_macros.h"
TEST_FORCE_LINK(test_aspect_ratio_container)
#include "scene/gui/aspect_ratio_container.h"
#include "scene/gui/control.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
namespace TestAspectRatioContainer {
TEST_CASE("[SceneTree][AspectRatioContainer] Default Properties") {
AspectRatioContainer *aspect_ratio_container = memnew(AspectRatioContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(aspect_ratio_container);
Control *child_control = memnew(Control);
aspect_ratio_container->add_child(child_control);
CHECK_MESSAGE(
aspect_ratio_container->get_stretch_mode() == AspectRatioContainer::STRETCH_FIT,
"AspectRatioContainer stretch mode is set to STRETCH_FIT by default.");
CHECK_MESSAGE(
Math::is_equal_approx(aspect_ratio_container->get_ratio(), 1.0f),
"AspectRatioContainer ratio is set to 1.0 by default.");
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is positioned at the top-left corner of the AspectRatioContainer by default.");
CHECK_MESSAGE(
child_control->get_h_size_flags() == Control::SIZE_FILL,
"Child control horizontal size flags are set to SIZE_FILL by default.");
CHECK_MESSAGE(
child_control->get_v_size_flags() == Control::SIZE_FILL,
"Child control vertical size flags are set to SIZE_FILL by default.");
memdelete(child_control);
memdelete(aspect_ratio_container);
}
TEST_CASE("[SceneTree][AspectRatioContainer] Aspect Ratio") {
AspectRatioContainer *aspect_ratio_container = memnew(AspectRatioContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(aspect_ratio_container);
Control *child_control = memnew(Control);
aspect_ratio_container->add_child(child_control);
aspect_ratio_container->set_size(Size2(200, 100));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(100, 100)),
"Child control is resized to maintain aspect ratio when AspectRatioContainer is resized.");
aspect_ratio_container->set_size(Size2(100, 200));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(100, 100)),
"Child control is resized to maintain aspect ratio when AspectRatioContainer is resized.");
aspect_ratio_container->set_ratio(2.0f);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(100, 50)),
"Child control is resized to maintain aspect ratio when AspectRatioContainer ratio is changed.");
aspect_ratio_container->set_ratio(0.5f);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(100, 200)),
"Child control is resized to maintain aspect ratio when AspectRatioContainer ratio is changed.");
aspect_ratio_container->set_size(Size2(200, 100));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(50, 100)),
"Child control is resized to maintain aspect ratio when AspectRatioContainer is resized after ratio change.");
memdelete(child_control);
memdelete(aspect_ratio_container);
}
TEST_CASE("[SceneTree][AspectRatioContainer] Stretch Modes") {
AspectRatioContainer *aspect_ratio_container = memnew(AspectRatioContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(aspect_ratio_container);
Control *child_control = memnew(Control);
aspect_ratio_container->add_child(child_control);
aspect_ratio_container->set_size(Size2(200, 100));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(100, 100)),
"Child control is resized to maintain aspect ratio in STRETCH_FIT mode.");
aspect_ratio_container->set_stretch_mode(AspectRatioContainer::STRETCH_WIDTH_CONTROLS_HEIGHT);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(200, 200)),
"Child control height is adjusted to maintain aspect ratio in STRETCH_WIDTH_CONTROLS_HEIGHT mode.");
aspect_ratio_container->set_stretch_mode(AspectRatioContainer::STRETCH_HEIGHT_CONTROLS_WIDTH);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(100, 100)),
"Child control width is adjusted to maintain aspect ratio in STRETCH_HEIGHT_CONTROLS_WIDTH mode.");
aspect_ratio_container->set_stretch_mode(AspectRatioContainer::STRETCH_COVER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(200, 200)),
"Child control is resized to cover the entire AspectRatioContainer in STRETCH_COVER mode.");
aspect_ratio_container->set_size(Size2(100, 200));
aspect_ratio_container->set_stretch_mode(AspectRatioContainer::STRETCH_FIT);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
aspect_ratio_container->get_stretch_mode() == AspectRatioContainer::STRETCH_FIT,
"AspectRatioContainer stretch mode is set to STRETCH_FIT by default.");
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(100, 100)),
"Child control is resized to maintain aspect ratio in STRETCH_FIT mode.");
aspect_ratio_container->set_stretch_mode(AspectRatioContainer::STRETCH_WIDTH_CONTROLS_HEIGHT);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(100, 100)),
"Child control height is adjusted to maintain aspect ratio in STRETCH_WIDTH_CONTROLS_HEIGHT mode.");
aspect_ratio_container->set_stretch_mode(AspectRatioContainer::STRETCH_HEIGHT_CONTROLS_WIDTH);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(200, 200)),
"Child control width is adjusted to maintain aspect ratio in STRETCH_HEIGHT_CONTROLS_WIDTH mode.");
aspect_ratio_container->set_stretch_mode(AspectRatioContainer::STRETCH_COVER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(200, 200)),
"Child control is resized to cover the entire AspectRatioContainer in STRETCH_COVER mode.");
memdelete(child_control);
memdelete(aspect_ratio_container);
}
TEST_CASE("[SceneTree][AspectRatioContainer] Alignment modes") {
AspectRatioContainer *aspect_ratio_container = memnew(AspectRatioContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(aspect_ratio_container);
Control *child_control = memnew(Control);
aspect_ratio_container->add_child(child_control);
SUBCASE("Horizontal Alignment") {
aspect_ratio_container->set_size(Size2(200, 100));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
aspect_ratio_container->get_alignment_horizontal() == AspectRatioContainer::ALIGNMENT_CENTER,
"AspectRatioContainer horizontal alignment is set to ALIGNMENT_CENTER by default.");
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(50, 0)),
"Child control is aligned to the center in ALIGNMENT_CENTER horizontal alignment mode.");
aspect_ratio_container->set_alignment_horizontal(AspectRatioContainer::ALIGNMENT_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left in ALIGNMENT_BEGIN horizontal alignment mode.");
aspect_ratio_container->set_alignment_horizontal(AspectRatioContainer::ALIGNMENT_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(100, 0)),
"Child control is aligned to the right in ALIGNMENT_END horizontal alignment mode.");
}
SUBCASE("Vertical Alignment") {
aspect_ratio_container->set_size(Size2(100, 200));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
aspect_ratio_container->get_alignment_vertical() == AspectRatioContainer::ALIGNMENT_CENTER,
"AspectRatioContainer vertical alignment is set to ALIGNMENT_CENTER by default.");
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 50)),
"Child control is aligned to the center in ALIGNMENT_CENTER vertical alignment mode.");
aspect_ratio_container->set_alignment_vertical(AspectRatioContainer::ALIGNMENT_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the top in ALIGNMENT_BEGIN vertical alignment mode.");
aspect_ratio_container->set_alignment_vertical(AspectRatioContainer::ALIGNMENT_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 100)),
"Child control is aligned to the bottom in ALIGNMENT_END vertical alignment mode.");
}
memdelete(child_control);
memdelete(aspect_ratio_container);
}
TEST_CASE("[SceneTree][AspectRatioContainer] Container Sizing Flags") {
AspectRatioContainer *aspect_ratio_container = memnew(AspectRatioContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(aspect_ratio_container);
Control *child_control = memnew(Control);
aspect_ratio_container->add_child(child_control);
SUBCASE("Horizontal Flags") {
aspect_ratio_container->set_size(Size2(200, 100));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(50, 0)),
"Child control is vertically centered by default with SIZE_FILL.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(50, 0)),
"Child control is aligned to the left of its would-be area with SIZE_FILL in SIZE_SHRINK_BEGIN horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(100, 0)),
"Child control is centered in its would-be area with SIZE_FILL in SIZE_SHRINK_CENTER horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(150, 0)),
"Child control is aligned to the right of its would-be area with SIZE_FILL in SIZE_SHRINK_END horizontal size flag mode.");
}
SUBCASE("Horizontal Flags RTL") {
aspect_ratio_container->set_size(Size2(200, 100));
aspect_ratio_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(50, 0)),
"Child control is vertically centered by default with SIZE_FILL.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(150, 0)),
"Child control is aligned to the right of its would-be area with SIZE_FILL in RTL SIZE_SHRINK_BEGIN horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(100, 0)),
"Child control is centered in its would-be area with SIZE_FILL in SIZE_SHRINK_CENTER horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(50, 0)),
"Child control is aligned to the left of its would-be area with SIZE_FILL in RTL SIZE_SHRINK_END horizontal size flag mode.");
}
SUBCASE("Vertical Flags") {
aspect_ratio_container->set_size(Size2(100, 200));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 50)),
"Child control is horizontally centered by default with SIZE_FILL.");
child_control->set_v_size_flags(Control::SIZE_SHRINK_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 50)),
"Child control is aligned to the top of its would-be area with SIZE_FILL in SIZE_SHRINK_BEGIN vertical size flag mode.");
child_control->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 100)),
"Child control is centered in its would-be area with SIZE_FILL in SIZE_SHRINK_CENTER vertical size flag mode.");
child_control->set_v_size_flags(Control::SIZE_SHRINK_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 150)),
"Child control is aligned to the bottom of its would-be area with SIZE_FILL in SIZE_SHRINK_END vertical size flag mode.");
}
memdelete(child_control);
memdelete(aspect_ratio_container);
}
} // namespace TestAspectRatioContainer
+625
View File
@@ -0,0 +1,625 @@
/**************************************************************************/
/* test_box_container.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. */
/**************************************************************************/
#include "tests/test_macros.h"
TEST_FORCE_LINK(test_box_container)
#include "scene/gui/box_container.h"
#include "scene/gui/control.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
namespace TestBoxContainer {
TEST_CASE("[SceneTree][BoxContainer] HBoxContainer") {
HBoxContainer *hbox_container = memnew(HBoxContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(hbox_container);
hbox_container->set_size(Size2(100, 100));
SceneTree::get_singleton()->process(0);
SUBCASE("Default behavior") {
Control *child_control = memnew(Control);
hbox_container->add_child(child_control);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
hbox_container->get_alignment() == BoxContainer::ALIGNMENT_BEGIN,
"HBoxContainer alignment is set to ALIGNMENT_BEGIN by default.");
CHECK_MESSAGE(
!hbox_container->is_vertical(),
"HBoxContainer orientation is horizontal.");
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left in ALIGNMENT_BEGIN mode.");
CHECK_MESSAGE(
child_control->get_h_size_flags() == Control::SIZE_FILL,
"Child control horizontal size flags are set to SIZE_FILL by default.");
CHECK_MESSAGE(
child_control->get_v_size_flags() == Control::SIZE_FILL,
"Child control vertical size flags are set to SIZE_FILL by default.");
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(0, 100)),
"Child control takes up minimum horizontal space and matches container height by default.");
memdelete(child_control);
}
SUBCASE("Child Horizontal Size Flags") {
Control *child_control = memnew(Control);
hbox_container->add_child(child_control);
child_control->set_custom_minimum_size(Size2(20, 20));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left by default with SIZE_FILL.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left of in SIZE_SHRINK_BEGIN horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left of in SIZE_SHRINK_CENTER horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left of in SIZE_SHRINK_END horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_EXPAND | Control::SIZE_SHRINK_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left in SIZE_EXPAND | SIZE_SHRINK_BEGIN horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_EXPAND | Control::SIZE_SHRINK_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(40, 0)),
"Child control is aligned to the center in SIZE_EXPAND | SIZE_SHRINK_CENTER horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_EXPAND | Control::SIZE_SHRINK_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(80, 0)),
"Child control is aligned to the right in SIZE_EXPAND | SIZE_SHRINK_END horizontal size flag mode.");
memdelete(child_control);
}
SUBCASE("Child Vertical Size Flags") {
Control *child_control = memnew(Control);
hbox_container->add_child(child_control);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the top by default with SIZE_FILL.");
child_control->set_v_size_flags(Control::SIZE_SHRINK_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the top of in SIZE_SHRINK_BEGIN vertical size flag mode.");
child_control->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 50)),
"Child control is aligned to the center of in SIZE_SHRINK_CENTER vertical size flag mode.");
child_control->set_v_size_flags(Control::SIZE_SHRINK_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 100)),
"Child control is aligned to the bottom of in SIZE_SHRINK_END vertical size flag mode.");
memdelete(child_control);
}
SUBCASE("Child Alignment") {
Control *child_control = memnew(Control);
hbox_container->add_child(child_control);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left in ALIGNMENT_BEGIN mode by default.");
hbox_container->set_alignment(BoxContainer::ALIGNMENT_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(50, 0)),
"Child control is aligned to the center in ALIGNMENT_CENTER mode.");
hbox_container->set_alignment(BoxContainer::ALIGNMENT_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(100, 0)),
"Child control is aligned to the right in ALIGNMENT_END mode.");
memdelete(child_control);
}
SUBCASE("Child Alignment RTL") {
Control *child_control = memnew(Control);
hbox_container->add_child(child_control);
hbox_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(100, 0)),
"Child control is aligned to the right in RTL ALIGNMENT_BEGIN mode by default.");
hbox_container->set_alignment(BoxContainer::ALIGNMENT_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(50, 0)),
"Child control is aligned to the center in RTL ALIGNMENT_CENTER mode.");
hbox_container->set_alignment(BoxContainer::ALIGNMENT_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the right in RTL ALIGNMENT_END mode.");
memdelete(child_control);
}
SUBCASE("Child Separation") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
hbox_container->add_child(child_control_1);
hbox_container->add_child(child_control_2);
hbox_container->add_theme_constant_override("separation", 10);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(10, 0)),
"Child controls are separated by the specified separation value.");
memdelete(child_control_1);
memdelete(child_control_2);
}
SUBCASE("Spacers") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
hbox_container->add_child(child_control_1);
hbox_container->add_spacer();
hbox_container->add_child(child_control_2);
hbox_container->add_theme_constant_override("separation", 0);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(100, 0)),
"Spacer pushes child controls apart.");
memdelete(child_control_1);
memdelete(child_control_2);
}
SUBCASE("Expanding children") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
hbox_container->add_child(child_control_1);
hbox_container->add_child(child_control_2);
hbox_container->add_theme_constant_override("separation", 0);
child_control_1->set_h_size_flags(Control::SIZE_EXPAND_FILL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(100, 100)),
"One child control expands to fill available space.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(0, 100)),
"The non-expanding child control remains at minimum size.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"Expanding child control is aligned to the left.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(100, 0)),
"Non-expanding child control is positioned after the expanding child control.");
child_control_2->set_custom_minimum_size(Size2(20, 0));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(80, 100)),
"Expanding child control shrinks to accommodate the custom minimum size of the other child.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(20, 100)),
"The non-expanding child control respects its custom minimum size.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"Expanding child control is aligned to the left.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(80, 0)),
"Non-expanding child control is positioned after the expanding child control.");
child_control_2->set_h_size_flags(Control::SIZE_EXPAND_FILL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(50, 100)),
"Both child controls expand equally to fill available space.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(50, 100)),
"Both child controls expand equally to fill available space.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First expanding child control is aligned to the left.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(50, 0)),
"Second expanding child control is positioned after the first expanding child control.");
child_control_2->set_stretch_ratio(3);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(25, 100)),
"Child control with lower stretch ratio takes less space.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(75, 100)),
"Child control with higher stretch ratio takes more space.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First expanding child control is aligned to the left.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(25, 0)),
"Second expanding child control is positioned after the first expanding child control.");
memdelete(child_control_1);
memdelete(child_control_2);
}
memdelete(hbox_container);
}
TEST_CASE("[SceneTree][BoxContainer] VBoxContainer") {
VBoxContainer *vbox_container = memnew(VBoxContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(vbox_container);
vbox_container->set_size(Size2(100, 100));
SceneTree::get_singleton()->process(0);
SUBCASE("Default behavior") {
Control *child_control = memnew(Control);
vbox_container->add_child(child_control);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
vbox_container->get_alignment() == BoxContainer::ALIGNMENT_BEGIN,
"HBoxContainer alignment is set to ALIGNMENT_BEGIN by default.");
CHECK_MESSAGE(
vbox_container->is_vertical(),
"VBoxContainer orientation is vertical.");
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left in ALIGNMENT_BEGIN mode.");
CHECK_MESSAGE(
child_control->get_h_size_flags() == Control::SIZE_FILL,
"Child control horizontal size flags are set to SIZE_FILL by default.");
CHECK_MESSAGE(
child_control->get_v_size_flags() == Control::SIZE_FILL,
"Child control vertical size flags are set to SIZE_FILL by default.");
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(100, 0)),
"Child control takes up minimum vertical space and matches container width by default.");
memdelete(child_control);
}
SUBCASE("Child Size Flags") {
Control *child_control = memnew(Control);
vbox_container->add_child(child_control);
child_control->set_custom_minimum_size(Size2(20, 20));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the top by default with SIZE_FILL.");
child_control->set_v_size_flags(Control::SIZE_SHRINK_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the top of in SIZE_SHRINK_BEGIN vertical size flag mode.");
child_control->set_v_size_flags(Control::SIZE_SHRINK_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the top of in SIZE_SHRINK_CENTER vertical size flag mode.");
child_control->set_v_size_flags(Control::SIZE_SHRINK_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the top of in SIZE_SHRINK_END vertical size flag mode.");
child_control->set_v_size_flags(Control::SIZE_EXPAND | Control::SIZE_SHRINK_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the top in SIZE_EXPAND | SIZE_SHRINK_BEGIN vertical size flag mode.");
child_control->set_v_size_flags(Control::SIZE_EXPAND | Control::SIZE_SHRINK_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 40)),
"Child control is aligned to the center in SIZE_EXPAND | SIZE_SHRINK_CENTER vertical size flag mode.");
child_control->set_v_size_flags(Control::SIZE_EXPAND | Control::SIZE_SHRINK_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 80)),
"Child control is aligned to the bottom in SIZE_EXPAND | SIZE_SHRINK_END vertical size flag mode.");
memdelete(child_control);
}
SUBCASE("Child Horizontal Size Flags") {
Control *child_control = memnew(Control);
vbox_container->add_child(child_control);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left by default with SIZE_FILL.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_BEGIN);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the left of in SIZE_SHRINK_BEGIN horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(50, 0)),
"Child control is aligned to the center of in SIZE_SHRINK_CENTER horizontal size flag mode.");
child_control->set_h_size_flags(Control::SIZE_SHRINK_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(100, 0)),
"Child control is aligned to the right of in SIZE_SHRINK_END horizontal size flag mode.");
memdelete(child_control);
}
SUBCASE("Child Alignment") {
Control *child_control = memnew(Control);
vbox_container->add_child(child_control);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the top in ALIGNMENT_BEGIN mode by default.");
vbox_container->set_alignment(BoxContainer::ALIGNMENT_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 50)),
"Child control is aligned to the center in ALIGNMENT_CENTER mode.");
vbox_container->set_alignment(BoxContainer::ALIGNMENT_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 100)),
"Child control is aligned to the bottom in ALIGNMENT_END mode.");
memdelete(child_control);
}
SUBCASE("Child Separation") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
vbox_container->add_child(child_control_1);
vbox_container->add_child(child_control_2);
vbox_container->add_theme_constant_override("separation", 10);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 10)),
"Child controls are separated by the specified separation value.");
memdelete(child_control_1);
memdelete(child_control_2);
}
SUBCASE("Spacers") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
vbox_container->add_child(child_control_1);
vbox_container->add_spacer();
vbox_container->add_child(child_control_2);
vbox_container->add_theme_constant_override("separation", 0);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 100)),
"Spacer pushes child controls apart.");
memdelete(child_control_1);
memdelete(child_control_2);
}
SUBCASE("Expanding children") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
vbox_container->add_child(child_control_1);
vbox_container->add_child(child_control_2);
vbox_container->add_theme_constant_override("separation", 0);
child_control_1->set_v_size_flags(Control::SIZE_EXPAND_FILL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(100, 100)),
"One child control expands to fill available space.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(100, 0)),
"The non-expanding child control remains at minimum size.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"Expanding child control is aligned to the left.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 100)),
"Non-expanding child control is positioned after the expanding child control.");
child_control_2->set_custom_minimum_size(Size2(0, 20));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(100, 80)),
"Expanding child control shrinks to accommodate the custom minimum size of the other child.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(100, 20)),
"The non-expanding child control respects its custom minimum size.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"Expanding child control is aligned to the left.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 80)),
"Non-expanding child control is positioned after the expanding child control.");
child_control_2->set_v_size_flags(Control::SIZE_EXPAND_FILL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(100, 50)),
"Both child controls expand equally to fill available space.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(100, 50)),
"Both child controls expand equally to fill available space.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First expanding child control is aligned to the left.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 50)),
"Second expanding child control is positioned after the first expanding child control.");
child_control_2->set_stretch_ratio(3);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(100, 25)),
"Child control with lower stretch ratio takes less space.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(100, 75)),
"Child control with higher stretch ratio takes more space.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First expanding child control is aligned to the left.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 25)),
"Second expanding child control is positioned after the first expanding child control.");
memdelete(child_control_1);
memdelete(child_control_2);
}
memdelete(vbox_container);
}
} // namespace TestBoxContainer
+170
View File
@@ -0,0 +1,170 @@
/**************************************************************************/
/* test_center_container.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. */
/**************************************************************************/
#include "tests/test_macros.h"
TEST_FORCE_LINK(test_center_container)
#include "scene/gui/center_container.h"
#include "scene/gui/control.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
namespace TestCenterContainer {
TEST_CASE("[SceneTree][CenterContainer] Standard behavior") {
CenterContainer *center_container = memnew(CenterContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(center_container);
Control *child_control = memnew(Control);
center_container->add_child(child_control);
center_container->set_size(Size2(100, 100));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(50, 50)),
"Child control is centered within the CenterContainer by default.");
child_control->set_custom_minimum_size(Size2(20, 20));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(40, 40)),
"Child control remains centered when custom minimum size is set.");
child_control->set_custom_minimum_size(Size2(200, 200));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
center_container->get_size().is_equal_approx(Size2(200, 200)),
"CenterContainer expands to accommodate the minimum size of its child control.");
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is positioned at the top-left corner when its minimum size equals the container size.");
child_control->set_custom_minimum_size(Size2());
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(50, 50)),
"Child control re-centers when custom minimum size is reset.");
CHECK_MESSAGE(
center_container->get_size().is_equal_approx(Size2(100, 100)),
"CenterContainer returns to original size when child control's custom minimum size is reset.");
center_container->set_use_top_left(true);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control is aligned to the top-left corner when use_top_left is enabled.");
center_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(100, 0)),
"Child control is aligned to the top-right corner in RTL layout direction when use_top_left is enabled.");
memdelete(child_control);
memdelete(center_container);
}
TEST_CASE("[SceneTree][CenterContainer] Multiple children") {
CenterContainer *center_container = memnew(CenterContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(center_container);
center_container->set_size(Size2(100, 100));
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
center_container->add_child(child_control_1);
center_container->add_child(child_control_2);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(50, 50)),
"First child control is centered within the CenterContainer.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(50, 50)),
"Second child control is also centered and overlaps the first child control by default.");
child_control_1->set_custom_minimum_size(Size2(20, 20));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(40, 40)),
"First child control remains centered when custom minimum size is set.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(50, 50)),
"Second child control remains centered and overlaps the first child control even when the first child has a custom minimum size.");
child_control_1->set_custom_minimum_size(Size2(200, 0));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
center_container->get_size().is_equal_approx(Size2(200, 100)),
"CenterContainer expands to accommodate the minimum size of the first child control.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 50)),
"First child control is aligned to the left edge of the CenterContainer when its minimum width equals the container width.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(100, 50)),
"Second child control is centered on the new container center.");
child_control_2->set_custom_minimum_size(Size2(0, 200));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
center_container->get_size().is_equal_approx(Size2(200, 200)),
"CenterContainer expands to accommodate the minimum size of the second child control.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 100)),
"First child control is aligned to the left edge and centered vertically when its minimum width equals the container width but its minimum height is smaller than the container height.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(100, 0)),
"Second child control is aligned to the top edge and centered horizontally when its minimum height equals the container height but its minimum width is smaller than the container width.");
memdelete(child_control_2);
memdelete(child_control_1);
memdelete(center_container);
}
} // namespace TestCenterContainer
+576
View File
@@ -0,0 +1,576 @@
/**************************************************************************/
/* test_flow_container.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. */
/**************************************************************************/
#include "tests/test_macros.h"
TEST_FORCE_LINK(test_flow_container)
#include "scene/gui/control.h"
#include "scene/gui/flow_container.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
namespace TestFlowContainer {
TEST_CASE("[SceneTree][FlowContainer] HFlowContainer") {
HFlowContainer *hflow_container = memnew(HFlowContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(hflow_container);
hflow_container->add_theme_constant_override("h_separation", 0);
hflow_container->add_theme_constant_override("v_separation", 0);
hflow_container->set_size(Size2(100, 100));
SceneTree::get_singleton()->process(0);
SUBCASE("Default behavior") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
Control *child_control_3 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 10));
child_control_2->set_custom_minimum_size(Size2(20, 10));
child_control_3->set_custom_minimum_size(Size2(20, 10));
hflow_container->add_child(child_control_1);
hflow_container->add_child(child_control_2);
hflow_container->add_child(child_control_3);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
hflow_container->get_alignment() == FlowContainer::ALIGNMENT_BEGIN,
"HFlowContainer alignment is set to ALIGNMENT_BEGIN by default.");
CHECK_MESSAGE(
hflow_container->get_last_wrap_alignment() == FlowContainer::LAST_WRAP_ALIGNMENT_INHERIT,
"HFlowContainer last wrap alignment is set to LAST_WRAP_ALIGNMENT_INHERIT by default.");
CHECK_MESSAGE(
!hflow_container->is_vertical(),
"HFlowContainer is horizontal.");
CHECK_MESSAGE(
!hflow_container->is_reverse_fill(),
"HFlowContainer reverse fill is disabled by default.");
CHECK_MESSAGE(
hflow_container->get_line_count() == 1,
"HFlowContainer keeps all children in one line when there is enough horizontal space.");
CHECK_MESSAGE(
hflow_container->get_line_max_child_count() == 3,
"HFlowContainer reports the child count of the first line.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First child control is positioned at the beginning of the line.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(20, 0)),
"Second child control is placed after the first child control.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(40, 0)),
"Third child control is placed after the second child control.");
memdelete(child_control_3);
memdelete(child_control_2);
memdelete(child_control_1);
}
SUBCASE("Wrapping and last wrap alignment") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
Control *child_control_3 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 10));
child_control_2->set_custom_minimum_size(Size2(20, 10));
child_control_3->set_custom_minimum_size(Size2(20, 10));
hflow_container->set_size(Size2(50, 100));
hflow_container->add_child(child_control_1);
hflow_container->add_child(child_control_2);
hflow_container->add_child(child_control_3);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
hflow_container->get_line_count() == 2,
"HFlowContainer wraps children to a second line when they no longer fit.");
CHECK_MESSAGE(
hflow_container->get_line_max_child_count() == 2,
"HFlowContainer reports the first line child count as the line max child count.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First child control starts at the first line origin.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(20, 0)),
"Second child control remains on the first line.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(0, 10)),
"Third child control wraps to the second line.");
hflow_container->set_last_wrap_alignment(FlowContainer::LAST_WRAP_ALIGNMENT_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(10, 10)),
"Last wrapped line is centered relative to the previous line when LAST_WRAP_ALIGNMENT_CENTER is used.");
hflow_container->set_last_wrap_alignment(FlowContainer::LAST_WRAP_ALIGNMENT_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(20, 10)),
"Last wrapped line is aligned to the end relative to the previous line when LAST_WRAP_ALIGNMENT_END is used.");
memdelete(child_control_3);
memdelete(child_control_2);
memdelete(child_control_1);
}
SUBCASE("Alignment and RTL") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 10));
child_control_2->set_custom_minimum_size(Size2(20, 10));
hflow_container->set_size(Size2(100, 100));
hflow_container->add_child(child_control_1);
hflow_container->add_child(child_control_2);
SceneTree::get_singleton()->process(0);
hflow_container->set_alignment(FlowContainer::ALIGNMENT_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(30, 0)),
"First child control is centered when ALIGNMENT_CENTER is used.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(50, 0)),
"Second child control follows centered alignment when ALIGNMENT_CENTER is used.");
hflow_container->set_alignment(FlowContainer::ALIGNMENT_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(60, 0)),
"First child control is aligned to the end when ALIGNMENT_END is used.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(80, 0)),
"Second child control is aligned to the end when ALIGNMENT_END is used.");
hflow_container->set_alignment(FlowContainer::ALIGNMENT_BEGIN);
hflow_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(80, 0)),
"First child control starts at the right edge in RTL mode with ALIGNMENT_BEGIN.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(60, 0)),
"Second child control follows right-to-left ordering in RTL mode with ALIGNMENT_BEGIN.");
memdelete(child_control_2);
memdelete(child_control_1);
}
SUBCASE("Expanding children and stretch ratio") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 10));
child_control_2->set_custom_minimum_size(Size2(20, 10));
child_control_1->set_h_size_flags(Control::SIZE_EXPAND_FILL);
child_control_2->set_h_size_flags(Control::SIZE_EXPAND_FILL);
hflow_container->set_size(Size2(100, 100));
hflow_container->add_child(child_control_1);
hflow_container->add_child(child_control_2);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(50, 10)),
"First child control expands to half of the available width when both children have equal stretch ratio.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(50, 10)),
"Second child control expands to half of the available width when both children have equal stretch ratio.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(50, 0)),
"Second child control is placed immediately after the first expanded child control.");
child_control_2->set_stretch_ratio(3.0f);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(35, 10)),
"First child control receives less width when its stretch ratio is lower.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(65, 10)),
"Second child control receives more width when its stretch ratio is higher.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(35, 0)),
"Second child control starts after the first child control's stretched width.");
memdelete(child_control_2);
memdelete(child_control_1);
}
SUBCASE("Reverse fill") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
Control *child_control_3 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 10));
child_control_2->set_custom_minimum_size(Size2(20, 10));
child_control_3->set_custom_minimum_size(Size2(20, 10));
hflow_container->set_size(Size2(50, 100));
hflow_container->add_child(child_control_1);
hflow_container->add_child(child_control_2);
hflow_container->add_child(child_control_3);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First child control starts at the top row by default.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(0, 10)),
"Third child control wraps to the next row by default.");
hflow_container->set_reverse_fill(true);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 90)),
"First child control moves to the bottom row when reverse fill is enabled.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(20, 90)),
"Second child control remains in the same row as the first child control when reverse fill is enabled.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(0, 80)),
"Wrapped row order is inverted from bottom to top when reverse fill is enabled.");
hflow_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(30, 90)),
"RTL mirrors horizontal placement while reverse fill keeps rows bottom to top.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(10, 90)),
"Second child control follows mirrored RTL order while reverse fill remains enabled.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(30, 80)),
"Wrapped child control is mirrored in RTL while keeping reverse-filled row order.");
memdelete(child_control_3);
memdelete(child_control_2);
memdelete(child_control_1);
}
memdelete(hflow_container);
}
TEST_CASE("[SceneTree][FlowContainer] VFlowContainer") {
VFlowContainer *vflow_container = memnew(VFlowContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(vflow_container);
vflow_container->add_theme_constant_override("h_separation", 0);
vflow_container->add_theme_constant_override("v_separation", 0);
vflow_container->set_size(Size2(100, 100));
SceneTree::get_singleton()->process(0);
SUBCASE("Default behavior") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
Control *child_control_3 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 10));
child_control_2->set_custom_minimum_size(Size2(20, 10));
child_control_3->set_custom_minimum_size(Size2(20, 10));
vflow_container->add_child(child_control_1);
vflow_container->add_child(child_control_2);
vflow_container->add_child(child_control_3);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
vflow_container->get_alignment() == FlowContainer::ALIGNMENT_BEGIN,
"VFlowContainer alignment is set to ALIGNMENT_BEGIN by default.");
CHECK_MESSAGE(
vflow_container->get_last_wrap_alignment() == FlowContainer::LAST_WRAP_ALIGNMENT_INHERIT,
"VFlowContainer last wrap alignment is set to LAST_WRAP_ALIGNMENT_INHERIT by default.");
CHECK_MESSAGE(
vflow_container->is_vertical(),
"VFlowContainer is vertical.");
CHECK_MESSAGE(
!vflow_container->is_reverse_fill(),
"VFlowContainer reverse fill is disabled by default.");
CHECK_MESSAGE(
vflow_container->get_line_count() == 1,
"VFlowContainer keeps all children in one column when there is enough vertical space.");
CHECK_MESSAGE(
vflow_container->get_line_max_child_count() == 3,
"VFlowContainer reports the child count of the first column.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First child control is positioned at the top of the first column.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 10)),
"Second child control is placed below the first child control.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(0, 20)),
"Third child control is placed below the second child control.");
memdelete(child_control_3);
memdelete(child_control_2);
memdelete(child_control_1);
}
SUBCASE("Wrapping and last wrap alignment") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
Control *child_control_3 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 20));
child_control_2->set_custom_minimum_size(Size2(20, 20));
child_control_3->set_custom_minimum_size(Size2(20, 20));
vflow_container->set_size(Size2(100, 50));
vflow_container->add_child(child_control_1);
vflow_container->add_child(child_control_2);
vflow_container->add_child(child_control_3);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
vflow_container->get_line_count() == 2,
"VFlowContainer wraps children to a second column when they no longer fit vertically.");
CHECK_MESSAGE(
vflow_container->get_line_max_child_count() == 2,
"VFlowContainer reports the first column child count as the line max child count.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First child control starts at the first column origin.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 20)),
"Second child control remains in the first column.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(20, 0)),
"Third child control wraps to the second column.");
vflow_container->set_last_wrap_alignment(FlowContainer::LAST_WRAP_ALIGNMENT_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(20, 10)),
"Last wrapped column is centered relative to the previous column when LAST_WRAP_ALIGNMENT_CENTER is used.");
vflow_container->set_last_wrap_alignment(FlowContainer::LAST_WRAP_ALIGNMENT_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(20, 20)),
"Last wrapped column is aligned to the end relative to the previous column when LAST_WRAP_ALIGNMENT_END is used.");
memdelete(child_control_3);
memdelete(child_control_2);
memdelete(child_control_1);
}
SUBCASE("Alignment and RTL") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 20));
child_control_2->set_custom_minimum_size(Size2(20, 20));
vflow_container->set_size(Size2(100, 100));
vflow_container->add_child(child_control_1);
vflow_container->add_child(child_control_2);
SceneTree::get_singleton()->process(0);
vflow_container->set_alignment(FlowContainer::ALIGNMENT_CENTER);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 30)),
"First child control is vertically centered when ALIGNMENT_CENTER is used.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 50)),
"Second child control follows centered vertical alignment when ALIGNMENT_CENTER is used.");
vflow_container->set_alignment(FlowContainer::ALIGNMENT_END);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 60)),
"First child control is aligned to the bottom when ALIGNMENT_END is used.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 80)),
"Second child control is aligned to the bottom when ALIGNMENT_END is used.");
vflow_container->set_alignment(FlowContainer::ALIGNMENT_BEGIN);
vflow_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(80, 0)),
"First child control moves to the right edge in RTL mode with vertical flow.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(80, 20)),
"Second child control follows RTL horizontal mirroring in vertical flow.");
memdelete(child_control_2);
memdelete(child_control_1);
}
SUBCASE("Expanding children and stretch ratio") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 20));
child_control_2->set_custom_minimum_size(Size2(20, 20));
child_control_1->set_v_size_flags(Control::SIZE_EXPAND_FILL);
child_control_2->set_v_size_flags(Control::SIZE_EXPAND_FILL);
vflow_container->set_size(Size2(100, 100));
vflow_container->add_child(child_control_1);
vflow_container->add_child(child_control_2);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(20, 50)),
"First child control expands to half of the available height when both children have equal stretch ratio.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(20, 50)),
"Second child control expands to half of the available height when both children have equal stretch ratio.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 50)),
"Second child control is placed immediately after the first expanded child control.");
child_control_2->set_stretch_ratio(3.0f);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(20, 35)),
"First child control receives less height when its stretch ratio is lower.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(20, 65)),
"Second child control receives more height when its stretch ratio is higher.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 35)),
"Second child control starts after the first child control's stretched height.");
memdelete(child_control_2);
memdelete(child_control_1);
}
SUBCASE("Reverse fill") {
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
Control *child_control_3 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 20));
child_control_2->set_custom_minimum_size(Size2(20, 20));
child_control_3->set_custom_minimum_size(Size2(20, 20));
vflow_container->set_size(Size2(100, 30));
vflow_container->add_child(child_control_1);
vflow_container->add_child(child_control_2);
vflow_container->add_child(child_control_3);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First child control starts in the first column by default.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(20, 0)),
"Second child control starts in the second column by default.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(40, 0)),
"Third child control starts in the third column by default.");
vflow_container->set_reverse_fill(true);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(80, 0)),
"First child control starts from the right when reverse fill is enabled in vertical mode.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(60, 0)),
"Second child control follows right-to-left column filling when reverse fill is enabled.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(40, 0)),
"Third child control continues right-to-left column filling when reverse fill is enabled.");
vflow_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"Vertical flow with RTL and reverse fill fills columns from left to right.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(20, 0)),
"Second child control follows left-to-right order in vertical mode with RTL and reverse fill.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(40, 0)),
"Third child control follows left-to-right order in vertical mode with RTL and reverse fill.");
memdelete(child_control_3);
memdelete(child_control_2);
memdelete(child_control_1);
}
memdelete(vflow_container);
}
} // namespace TestFlowContainer
+270
View File
@@ -0,0 +1,270 @@
/**************************************************************************/
/* test_foldable_container.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. */
/**************************************************************************/
#include "tests/test_macros.h"
TEST_FORCE_LINK(test_foldable_container)
#ifndef ADVANCED_GUI_DISABLED
#include "scene/gui/control.h"
#include "scene/gui/foldable_container.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
namespace TestFoldableContainer {
TEST_CASE("[SceneTree][FoldableContainer] Default properties") {
FoldableContainer *foldable_container = memnew(FoldableContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(foldable_container);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!foldable_container->is_folded(),
"FoldableContainer is expanded by default.");
CHECK_MESSAGE(
foldable_container->get_title().is_empty(),
"FoldableContainer title is empty by default.");
CHECK_MESSAGE(
foldable_container->get_title_alignment() == HORIZONTAL_ALIGNMENT_LEFT,
"FoldableContainer title alignment is set to left by default.");
CHECK_MESSAGE(
foldable_container->get_title_position() == FoldableContainer::POSITION_TOP,
"FoldableContainer title position is set to top by default.");
CHECK_MESSAGE(
foldable_container->get_title_text_direction() == Control::TEXT_DIRECTION_AUTO,
"FoldableContainer title text direction is set to auto by default.");
CHECK_MESSAGE(
foldable_container->get_title_text_overrun_behavior() == TextServer::OVERRUN_NO_TRIMMING,
"FoldableContainer title overrun behavior is set to no trimming by default.");
CHECK_MESSAGE(
foldable_container->get_language().is_empty(),
"FoldableContainer language is empty by default.");
CHECK_MESSAGE(
foldable_container->get_focus_mode() == Control::FOCUS_ALL,
"FoldableContainer focus mode is set to FOCUS_ALL by default.");
CHECK_MESSAGE(
foldable_container->get_mouse_filter() == Control::MOUSE_FILTER_STOP,
"FoldableContainer mouse filter is set to MOUSE_FILTER_STOP by default.");
CHECK_MESSAGE(
foldable_container->get_foldable_group().is_null(),
"FoldableContainer has no foldable group by default.");
memdelete(foldable_container);
}
TEST_CASE("[SceneTree][FoldableContainer] Fold and expand behavior") {
FoldableContainer *foldable_container = memnew(FoldableContainer("Section"));
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(foldable_container);
Control *child_control = memnew(Control);
child_control->set_custom_minimum_size(Size2(120, 80));
foldable_container->add_child(child_control);
foldable_container->set_size(Size2(200, 200));
SceneTree::get_singleton()->process(0);
Size2 expanded_minimum_size = foldable_container->get_minimum_size();
CHECK_MESSAGE(
child_control->is_visible(),
"Child control is visible when FoldableContainer is expanded.");
foldable_container->fold();
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
foldable_container->is_folded(),
"FoldableContainer is folded after calling fold().");
CHECK_MESSAGE(
!child_control->is_visible(),
"Child control is hidden when FoldableContainer is folded.");
CHECK_MESSAGE(
foldable_container->get_minimum_size().y < expanded_minimum_size.y,
"Folded minimum size is smaller than expanded minimum size when content is present.");
foldable_container->expand();
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!foldable_container->is_folded(),
"FoldableContainer is expanded after calling expand().");
CHECK_MESSAGE(
child_control->is_visible(),
"Child control is visible again after FoldableContainer is expanded.");
foldable_container->set_folded(true);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
foldable_container->is_folded(),
"set_folded(true) folds the FoldableContainer.");
foldable_container->set_folded(false);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!foldable_container->is_folded(),
"set_folded(false) expands the FoldableContainer.");
memdelete(child_control);
memdelete(foldable_container);
}
TEST_CASE("[SceneTree][FoldableContainer] Title position and title bar controls") {
FoldableContainer *foldable_container = memnew(FoldableContainer("Section"));
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(foldable_container);
Control *content_control = memnew(Control);
content_control->set_custom_minimum_size(Size2(40, 40));
foldable_container->add_child(content_control);
Control *title_control = memnew(Control);
title_control->set_custom_minimum_size(Size2(30, 20));
foldable_container->add_title_bar_control(title_control);
foldable_container->set_size(Size2(200, 140));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
title_control->get_parent() == foldable_container,
"Title bar control is reparented to the FoldableContainer when added.");
real_t top_title_control_y = title_control->get_position().y;
real_t top_content_y = content_control->get_position().y;
foldable_container->set_title_position(FoldableContainer::POSITION_BOTTOM);
SceneTree::get_singleton()->process(0);
real_t bottom_title_control_y = title_control->get_position().y;
real_t bottom_content_y = content_control->get_position().y;
CHECK_MESSAGE(
bottom_title_control_y > top_title_control_y,
"Title bar controls move to the bottom when title position is set to POSITION_BOTTOM.");
CHECK_MESSAGE(
bottom_content_y < top_content_y,
"Content area shifts upward when title position is changed from top to bottom.");
foldable_container->remove_title_bar_control(title_control);
CHECK_MESSAGE(
title_control->get_parent() == nullptr,
"Title bar control is detached from FoldableContainer when removed.");
memdelete(title_control);
memdelete(content_control);
memdelete(foldable_container);
}
TEST_CASE("[SceneTree][FoldableContainer] FoldableGroup behavior") {
Ref<FoldableGroup> foldable_group;
foldable_group.instantiate();
FoldableContainer *container_a = memnew(FoldableContainer("A"));
FoldableContainer *container_b = memnew(FoldableContainer("B"));
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(container_a);
root->add_child(container_b);
container_a->set_folded(true);
container_b->set_folded(true);
container_a->set_foldable_group(foldable_group);
container_b->set_foldable_group(foldable_group);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!container_a->is_folded(),
"First grouped FoldableContainer auto-expands when allow_folding_all is false and no container is expanded.");
CHECK_MESSAGE(
container_b->is_folded(),
"Second grouped FoldableContainer remains folded when another container in the group is expanded.");
CHECK_MESSAGE(
foldable_group->get_expanded_container() == container_a,
"FoldableGroup tracks the currently expanded container.");
container_b->set_folded(false);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
container_a->is_folded(),
"Expanding one grouped FoldableContainer folds the previously expanded container.");
CHECK_MESSAGE(
!container_b->is_folded(),
"Expanded grouped FoldableContainer remains unfolded.");
CHECK_MESSAGE(
foldable_group->get_expanded_container() == container_b,
"FoldableGroup updates expanded container after a different container is expanded.");
container_b->set_folded(true);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!container_b->is_folded(),
"Expanded grouped FoldableContainer cannot be folded when allow_folding_all is false.");
foldable_group->set_allow_folding_all(true);
container_b->set_folded(true);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
container_b->is_folded(),
"Grouped FoldableContainer can be folded when allow_folding_all is true.");
CHECK_MESSAGE(
foldable_group->get_expanded_container() == nullptr,
"FoldableGroup has no expanded container when all grouped containers are folded.");
memdelete(container_b);
memdelete(container_a);
}
} // namespace TestFoldableContainer
#endif
+265
View File
@@ -0,0 +1,265 @@
/**************************************************************************/
/* test_grid_container.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. */
/**************************************************************************/
#include "tests/test_macros.h"
TEST_FORCE_LINK(test_grid_container)
#include "scene/gui/control.h"
#include "scene/gui/grid_container.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
namespace TestGridContainer {
TEST_CASE("[SceneTree][GridContainer] Default properties") {
GridContainer *grid_container = memnew(GridContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(grid_container);
grid_container->set_size(Size2(200, 200));
grid_container->add_theme_constant_override("h_separation", 0);
grid_container->add_theme_constant_override("v_separation", 0);
Control *child_control = memnew(Control);
child_control->set_custom_minimum_size(Size2(20, 10));
grid_container->add_child(child_control);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
grid_container->get_columns() == 1,
"GridContainer columns are set to 1 by default.");
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Single child control starts at the top-left corner.");
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(20, 10)),
"Single child control keeps its minimum size when no expansion is requested.");
memdelete(child_control);
memdelete(grid_container);
}
TEST_CASE("[SceneTree][GridContainer] Columns and placement order") {
GridContainer *grid_container = memnew(GridContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(grid_container);
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
Control *child_control_3 = memnew(Control);
Control *child_control_4 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(10, 10));
child_control_2->set_custom_minimum_size(Size2(10, 10));
child_control_3->set_custom_minimum_size(Size2(10, 10));
child_control_4->set_custom_minimum_size(Size2(10, 10));
grid_container->set_columns(2);
grid_container->set_size(Size2(200, 200));
grid_container->add_theme_constant_override("h_separation", 0);
grid_container->add_theme_constant_override("v_separation", 0);
grid_container->add_child(child_control_1);
grid_container->add_child(child_control_2);
grid_container->add_child(child_control_3);
grid_container->add_child(child_control_4);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
grid_container->get_columns() == 2,
"GridContainer uses the configured number of columns.");
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First child control is placed at row 0, column 0.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(10, 0)),
"Second child control is placed at row 0, column 1.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(0, 10)),
"Third child control is placed at row 1, column 0.");
CHECK_MESSAGE(
child_control_4->get_position().is_equal_approx(Point2(10, 10)),
"Fourth child control is placed at row 1, column 1.");
memdelete(child_control_4);
memdelete(child_control_3);
memdelete(child_control_2);
memdelete(child_control_1);
memdelete(grid_container);
}
TEST_CASE("[SceneTree][GridContainer] Separation and minimum size") {
GridContainer *grid_container = memnew(GridContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(grid_container);
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
Control *child_control_3 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(10, 20));
child_control_2->set_custom_minimum_size(Size2(30, 10));
child_control_3->set_custom_minimum_size(Size2(15, 40));
grid_container->set_columns(2);
grid_container->add_theme_constant_override("h_separation", 5);
grid_container->add_theme_constant_override("v_separation", 7);
grid_container->add_child(child_control_1);
grid_container->add_child(child_control_2);
grid_container->add_child(child_control_3);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
grid_container->get_h_separation() == 5,
"GridContainer reports overridden horizontal separation.");
CHECK_MESSAGE(
grid_container->get_minimum_size().is_equal_approx(Size2(50, 67)),
"GridContainer minimum size matches per-column and per-row maxima plus separations.");
grid_container->set_size(Size2(50, 67));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(0, 0)),
"First child control is placed at row 0, column 0 with configured separations.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(20, 0)),
"Second child control is offset by column width and horizontal separation.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(0, 27)),
"Third child control is offset by row height and vertical separation.");
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(15, 20)),
"First child control fills the first grid cell dimensions.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(30, 20)),
"Second child control fills the second grid cell dimensions.");
CHECK_MESSAGE(
child_control_3->get_size().is_equal_approx(Size2(15, 40)),
"Third child control fills the second-row first-column cell dimensions.");
memdelete(child_control_3);
memdelete(child_control_2);
memdelete(child_control_1);
memdelete(grid_container);
}
TEST_CASE("[SceneTree][GridContainer] Expanding rows and columns") {
GridContainer *grid_container = memnew(GridContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(grid_container);
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
Control *child_control_3 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 20));
child_control_2->set_custom_minimum_size(Size2(20, 20));
child_control_3->set_custom_minimum_size(Size2(20, 20));
child_control_1->set_h_size_flags(Control::SIZE_EXPAND_FILL);
child_control_1->set_v_size_flags(Control::SIZE_EXPAND_FILL);
grid_container->set_columns(2);
grid_container->add_theme_constant_override("h_separation", 0);
grid_container->add_theme_constant_override("v_separation", 0);
grid_container->set_size(Size2(100, 100));
grid_container->add_child(child_control_1);
grid_container->add_child(child_control_2);
grid_container->add_child(child_control_3);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_size().is_equal_approx(Size2(80, 80)),
"Expanded child control grows with its expanded column and row.");
CHECK_MESSAGE(
child_control_2->get_size().is_equal_approx(Size2(20, 80)),
"Non-expanded column still receives expanded row height.");
CHECK_MESSAGE(
child_control_3->get_size().is_equal_approx(Size2(80, 20)),
"Non-expanded row still receives expanded column width.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(80, 0)),
"Second child control is positioned after expanded first column width.");
CHECK_MESSAGE(
child_control_3->get_position().is_equal_approx(Point2(0, 80)),
"Third child control is positioned after expanded first row height.");
memdelete(child_control_3);
memdelete(child_control_2);
memdelete(child_control_1);
memdelete(grid_container);
}
TEST_CASE("[SceneTree][GridContainer] RTL placement") {
GridContainer *grid_container = memnew(GridContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(grid_container);
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(20, 20));
child_control_2->set_custom_minimum_size(Size2(20, 20));
grid_container->set_columns(2);
grid_container->add_theme_constant_override("h_separation", 0);
grid_container->add_theme_constant_override("v_separation", 0);
grid_container->set_size(Size2(40, 20));
grid_container->add_child(child_control_1);
grid_container->add_child(child_control_2);
grid_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control_1->get_position().is_equal_approx(Point2(20, 0)),
"First child control starts in the right column in RTL mode.");
CHECK_MESSAGE(
child_control_2->get_position().is_equal_approx(Point2(0, 0)),
"Second child control is placed in the left column in RTL mode.");
memdelete(child_control_2);
memdelete(child_control_1);
memdelete(grid_container);
}
} // namespace TestGridContainer
+146
View File
@@ -0,0 +1,146 @@
/**************************************************************************/
/* test_margin_container.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. */
/**************************************************************************/
#include "tests/test_macros.h"
TEST_FORCE_LINK(test_margin_container)
#include "scene/gui/control.h"
#include "scene/gui/margin_container.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
namespace TestMarginContainer {
TEST_CASE("[SceneTree][MarginContainer] Default margins and layout") {
MarginContainer *margin_container = memnew(MarginContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(margin_container);
Control *child_control = memnew(Control);
child_control->set_custom_minimum_size(Size2(20, 10));
margin_container->add_child(child_control);
margin_container->set_size(Size2(100, 80));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
margin_container->get_margin_size(SIDE_LEFT) == 0,
"Left margin is 0 by default.");
CHECK_MESSAGE(
margin_container->get_margin_size(SIDE_TOP) == 0,
"Top margin is 0 by default.");
CHECK_MESSAGE(
margin_container->get_margin_size(SIDE_RIGHT) == 0,
"Right margin is 0 by default.");
CHECK_MESSAGE(
margin_container->get_margin_size(SIDE_BOTTOM) == 0,
"Bottom margin is 0 by default.");
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(0, 0)),
"Child control starts at the top-left corner when all margins are 0.");
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(100, 80)),
"Child control fills the container area when all margins are 0.");
CHECK_MESSAGE(
margin_container->get_minimum_size().is_equal_approx(Size2(20, 10)),
"Minimum size equals the child minimum size when all margins are 0.");
memdelete(child_control);
memdelete(margin_container);
}
TEST_CASE("[SceneTree][MarginContainer] Custom margins affect layout and minimum size") {
MarginContainer *margin_container = memnew(MarginContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(margin_container);
margin_container->add_theme_constant_override("margin_left", 10);
margin_container->add_theme_constant_override("margin_top", 20);
margin_container->add_theme_constant_override("margin_right", 30);
margin_container->add_theme_constant_override("margin_bottom", 40);
Control *child_control = memnew(Control);
child_control->set_custom_minimum_size(Size2(20, 10));
margin_container->add_child(child_control);
margin_container->set_size(Size2(200, 150));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(10, 20)),
"Child control is offset by left and top margins.");
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(160, 90)),
"Child control size is reduced by left/right and top/bottom margins.");
CHECK_MESSAGE(
margin_container->get_minimum_size().is_equal_approx(Size2(60, 70)),
"Minimum size equals child minimum size plus all configured margins.");
memdelete(child_control);
memdelete(margin_container);
}
TEST_CASE("[SceneTree][MarginContainer] Multiple children use maximum minimum size") {
MarginContainer *margin_container = memnew(MarginContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(margin_container);
margin_container->add_theme_constant_override("margin_left", 1);
margin_container->add_theme_constant_override("margin_top", 2);
margin_container->add_theme_constant_override("margin_right", 3);
margin_container->add_theme_constant_override("margin_bottom", 4);
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(40, 10));
child_control_2->set_custom_minimum_size(Size2(20, 50));
margin_container->add_child(child_control_1);
margin_container->add_child(child_control_2);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
margin_container->get_minimum_size().is_equal_approx(Size2(44, 56)),
"Minimum size uses maximum child minimum width/height plus margins.");
memdelete(child_control_2);
memdelete(child_control_1);
memdelete(margin_container);
}
} // namespace TestMarginContainer
+114
View File
@@ -0,0 +1,114 @@
/**************************************************************************/
/* test_panel_container.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. */
/**************************************************************************/
#include "tests/test_macros.h"
TEST_FORCE_LINK(test_panel_container)
#include "scene/gui/control.h"
#include "scene/gui/panel_container.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
#include "scene/resources/style_box.h"
namespace TestPanelContainer {
TEST_CASE("[SceneTree][PanelContainer] Default properties") {
PanelContainer *panel_container = memnew(PanelContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(panel_container);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
panel_container->get_mouse_filter() == Control::MOUSE_FILTER_STOP,
"PanelContainer mouse filter is set to MOUSE_FILTER_STOP by default.");
memdelete(panel_container);
}
TEST_CASE("[SceneTree][PanelContainer] StyleBox affects layout and minimum size") {
PanelContainer *panel_container = memnew(PanelContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(panel_container);
Ref<StyleBoxEmpty> panel_style = memnew(StyleBoxEmpty);
panel_style->set_content_margin_individual(5, 6, 7, 8);
panel_container->add_theme_style_override("panel", panel_style);
Control *child_control = memnew(Control);
child_control->set_custom_minimum_size(Size2(20, 10));
panel_container->add_child(child_control);
panel_container->set_size(Size2(100, 80));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
child_control->get_position().is_equal_approx(Point2(5, 6)),
"Child control is offset by the panel StyleBox margins.");
CHECK_MESSAGE(
child_control->get_size().is_equal_approx(Size2(88, 66)),
"Child control size is reduced by the panel StyleBox minimum size.");
CHECK_MESSAGE(
panel_container->get_minimum_size().is_equal_approx(Size2(32, 24)),
"Minimum size equals child minimum size plus panel StyleBox minimum size.");
memdelete(child_control);
memdelete(panel_container);
}
TEST_CASE("[SceneTree][PanelContainer] Multiple children use maximum minimum size") {
PanelContainer *panel_container = memnew(PanelContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(panel_container);
Ref<StyleBoxEmpty> panel_style = memnew(StyleBoxEmpty);
panel_style->set_content_margin_individual(2, 3, 4, 5);
panel_container->add_theme_style_override("panel", panel_style);
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
child_control_1->set_custom_minimum_size(Size2(40, 10));
child_control_2->set_custom_minimum_size(Size2(20, 50));
panel_container->add_child(child_control_1);
panel_container->add_child(child_control_2);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
panel_container->get_minimum_size().is_equal_approx(Size2(46, 58)),
"Minimum size uses maximum child minimum width/height plus panel StyleBox minimum size.");
memdelete(child_control_2);
memdelete(child_control_1);
memdelete(panel_container);
}
} // namespace TestPanelContainer
+475
View File
@@ -0,0 +1,475 @@
/**************************************************************************/
/* test_scroll_container.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. */
/**************************************************************************/
#include "tests/test_macros.h"
TEST_FORCE_LINK(test_scroll_container)
#include "scene/gui/control.h"
#include "scene/gui/scroll_container.h"
#include "scene/main/scene_tree.h"
#include "scene/main/window.h"
namespace TestScrollContainer {
TEST_CASE("[SceneTree][ScrollContainer] Default behavior") {
ScrollContainer *test_container = memnew(ScrollContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(test_container);
Control *test_control = memnew(Control);
test_control->set_custom_minimum_size(Size2(20, 20));
test_container->add_child(test_control);
test_container->set_size(Size2(100, 100));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
test_container->get_horizontal_scroll_mode() == ScrollContainer::SCROLL_MODE_AUTO,
"Horizontal scroll mode is SCROLL_MODE_AUTO by default.");
CHECK_MESSAGE(
test_container->get_vertical_scroll_mode() == ScrollContainer::SCROLL_MODE_AUTO,
"Vertical scroll mode is SCROLL_MODE_AUTO by default.");
CHECK_MESSAGE(
test_container->get_scroll_hint_mode() == ScrollContainer::SCROLL_HINT_MODE_DISABLED,
"Scroll hint mode is SCROLL_HINT_MODE_DISABLED by default.");
CHECK_MESSAGE(
!test_container->is_scroll_hint_tiled(),
"tile_scroll_hint is disabled by default.");
CHECK_MESSAGE(
!test_container->is_following_focus(),
"follow_focus is disabled by default.");
CHECK_MESSAGE(
!test_container->is_scroll_horizontal_by_default(),
"scroll_horizontal_by_default is disabled by default.");
CHECK_MESSAGE(
!test_container->get_draw_focus_border(),
"draw_focus_border is disabled by default.");
CHECK_MESSAGE(
test_container->is_clipping_contents(),
"clip_contents is enabled by default.");
CHECK_MESSAGE(
test_container->get_h_scroll() == 0,
"Horizontal scroll starts at 0 by default.");
CHECK_MESSAGE(
test_container->get_v_scroll() == 0,
"Vertical scroll starts at 0 by default.");
CHECK_MESSAGE(
test_container->get_h_scroll_bar() != nullptr,
"Horizontal scrollbar is created by default.");
CHECK_MESSAGE(
test_container->get_v_scroll_bar() != nullptr,
"Vertical scrollbar is created by default.");
CHECK_MESSAGE(
!test_container->get_h_scroll_bar()->is_visible(),
"Horizontal scrollbar is hidden in auto mode when content fits.");
CHECK_MESSAGE(
!test_container->get_v_scroll_bar()->is_visible(),
"Vertical scrollbar is hidden in auto mode when content fits.");
memdelete(test_control);
memdelete(test_container);
}
TEST_CASE("[SceneTree][ScrollContainer] Scroll modes") {
ScrollContainer *test_container = memnew(ScrollContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(test_container);
Control *test_control = memnew(Control);
test_container->add_child(test_control);
test_control->set_custom_minimum_size(Size2(100, 100));
Control *target_control = memnew(Control);
target_control->set_custom_minimum_size(Size2(20, 20));
target_control->set_position(Point2(80, 80));
target_control->set_size(Size2(20, 20));
test_control->add_child(target_control);
test_container->set_size(Size2(50, 50));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
test_container->get_h_scroll_bar()->is_visible(),
"H Scrollbar displays when ScrollContainer width is smaller than contents width with SCROLL_MODE_AUTO.");
CHECK_MESSAGE(
test_container->get_v_scroll_bar()->is_visible(),
"V Scrollbar displays when ScrollContainer height is smaller than contents height with SCROLL_MODE_AUTO.");
test_container->set_size(Size2(150, 150));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!test_container->get_h_scroll_bar()->is_visible(),
"H Scrollbar does not display when ScrollContainer width is larger than contents width with SCROLL_MODE_AUTO.");
CHECK_MESSAGE(
!test_container->get_v_scroll_bar()->is_visible(),
"V Scrollbar does not display when ScrollContainer height is larger than contents height with SCROLL_MODE_AUTO.");
test_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
test_container->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_DISABLED);
test_container->set_size(Size2(50, 50));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!test_container->get_h_scroll_bar()->is_visible(),
"H Scrollbar does not display when ScrollContainer width is smaller than contents width with SCROLL_MODE_DISABLED.");
CHECK_MESSAGE(
!test_container->get_v_scroll_bar()->is_visible(),
"V Scrollbar does not display when ScrollContainer height is smaller than contents height with SCROLL_MODE_DISABLED.");
test_container->set_h_scroll(0);
test_container->set_v_scroll(0);
int disabled_h_scroll_before = test_container->get_h_scroll();
int disabled_v_scroll_before = test_container->get_v_scroll();
test_container->ensure_control_visible(target_control);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
test_container->get_h_scroll() == disabled_h_scroll_before,
"SCROLL_MODE_DISABLED does not change horizontal scroll when ensure_control_visible() targets an out-of-view descendant.");
CHECK_MESSAGE(
test_container->get_v_scroll() == disabled_v_scroll_before,
"SCROLL_MODE_DISABLED does not change vertical scroll when ensure_control_visible() targets an out-of-view descendant.");
test_container->set_h_scroll(0);
test_container->set_v_scroll(0);
test_container->set_size(Size2(150, 150));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!test_container->get_h_scroll_bar()->is_visible(),
"H Scrollbar does not display when ScrollContainer width is larger than contents width with SCROLL_MODE_DISABLED.");
CHECK_MESSAGE(
!test_container->get_v_scroll_bar()->is_visible(),
"V Scrollbar does not display when ScrollContainer height is larger than contents height with SCROLL_MODE_DISABLED.");
test_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_SHOW_ALWAYS);
test_container->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_SHOW_ALWAYS);
test_container->set_size(Size2(50, 50));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
test_container->get_h_scroll_bar()->is_visible(),
"H Scrollbar displays when ScrollContainer width is smaller than contents width with SCROLL_MODE_SHOW_ALWAYS.");
CHECK_MESSAGE(
test_container->get_v_scroll_bar()->is_visible(),
"V Scrollbar displays when ScrollContainer height is smaller than contents height with SCROLL_MODE_SHOW_ALWAYS.");
test_container->set_size(Size2(150, 150));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
test_container->get_h_scroll_bar()->is_visible(),
"H Scrollbar displays when ScrollContainer width is larger than contents width with SCROLL_MODE_SHOW_ALWAYS.");
CHECK_MESSAGE(
test_container->get_v_scroll_bar()->is_visible(),
"V Scrollbar displays when ScrollContainer height is larger than contents height with SCROLL_MODE_SHOW_ALWAYS.");
test_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_SHOW_NEVER);
test_container->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_SHOW_NEVER);
test_container->set_size(Size2(50, 50));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!test_container->get_h_scroll_bar()->is_visible(),
"H Scrollbar does not display when ScrollContainer width is smaller than contents width with SCROLL_MODE_SHOW_NEVER.");
CHECK_MESSAGE(
!test_container->get_v_scroll_bar()->is_visible(),
"V Scrollbar does not display when ScrollContainer height is smaller than contents height with SCROLL_MODE_SHOW_NEVER.");
test_container->set_h_scroll(0);
test_container->set_v_scroll(0);
int show_never_h_scroll_before = test_container->get_h_scroll();
int show_never_v_scroll_before = test_container->get_v_scroll();
test_container->ensure_control_visible(target_control);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
test_container->get_h_scroll() > show_never_h_scroll_before,
"SCROLL_MODE_SHOW_NEVER keeps horizontal scrollbar hidden while still allowing programmatic scrolling.");
CHECK_MESSAGE(
test_container->get_v_scroll() > show_never_v_scroll_before,
"SCROLL_MODE_SHOW_NEVER keeps vertical scrollbar hidden while still allowing programmatic scrolling.");
test_container->set_h_scroll(0);
test_container->set_v_scroll(0);
test_container->set_size(Size2(150, 150));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!test_container->get_h_scroll_bar()->is_visible(),
"H Scrollbar does not display when ScrollContainer width is larger than contents width with SCROLL_MODE_SHOW_NEVER.");
CHECK_MESSAGE(
!test_container->get_v_scroll_bar()->is_visible(),
"V Scrollbar does not display when ScrollContainer height is larger than contents height with SCROLL_MODE_SHOW_NEVER.");
test_container->set_horizontal_scroll_mode(ScrollContainer::SCROLL_MODE_RESERVE);
test_container->set_vertical_scroll_mode(ScrollContainer::SCROLL_MODE_RESERVE);
test_container->set_size(Size2(50, 50));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
test_container->get_h_scroll_bar()->is_visible(),
"H Scrollbar displays when ScrollContainer width is smaller than contents width with SCROLL_MODE_RESERVE.");
CHECK_MESSAGE(
test_container->get_v_scroll_bar()->is_visible(),
"V Scrollbar displays when ScrollContainer height is smaller than contents height with SCROLL_MODE_RESERVE.");
test_container->set_size(Size2(150, 150));
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
!test_container->get_h_scroll_bar()->is_visible(),
"H Scrollbar does not display when ScrollContainer width is larger than contents width with SCROLL_MODE_RESERVE.");
CHECK_MESSAGE(
!test_container->get_v_scroll_bar()->is_visible(),
"V Scrollbar does not display when ScrollContainer height is larger than contents height with SCROLL_MODE_RESERVE.");
memdelete(test_control);
memdelete(test_container);
}
TEST_CASE("[SceneTree][ScrollContainer] Scroll values and property setters") {
ScrollContainer *test_container = memnew(ScrollContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(test_container);
Control *test_control = memnew(Control);
test_control->set_custom_minimum_size(Size2(300, 300));
test_container->add_child(test_control);
test_container->set_size(Size2(100, 100));
SceneTree::get_singleton()->process(0);
test_container->set_h_scroll(25);
test_container->set_v_scroll(35);
CHECK_MESSAGE(
test_container->get_h_scroll() == 25,
"Horizontal scroll value is updated by set_h_scroll().");
CHECK_MESSAGE(
test_container->get_v_scroll() == 35,
"Vertical scroll value is updated by set_v_scroll().");
test_container->set_horizontal_custom_step(7.0f);
test_container->set_vertical_custom_step(9.0f);
CHECK_MESSAGE(
Math::is_equal_approx(test_container->get_horizontal_custom_step(), 7.0f),
"Horizontal custom step is updated correctly.");
CHECK_MESSAGE(
Math::is_equal_approx(test_container->get_vertical_custom_step(), 9.0f),
"Vertical custom step is updated correctly.");
test_container->set_deadzone(13);
CHECK_MESSAGE(
test_container->get_deadzone() == 13,
"Deadzone value is updated correctly.");
test_container->set_scroll_horizontal_by_default(true);
CHECK_MESSAGE(
test_container->is_scroll_horizontal_by_default(),
"scroll_horizontal_by_default is enabled correctly.");
test_container->set_follow_focus(true);
CHECK_MESSAGE(
test_container->is_following_focus(),
"follow_focus is enabled correctly.");
test_container->set_scroll_hint_mode(ScrollContainer::SCROLL_HINT_MODE_ALL);
CHECK_MESSAGE(
test_container->get_scroll_hint_mode() == ScrollContainer::SCROLL_HINT_MODE_ALL,
"Scroll hint mode is updated correctly.");
test_container->set_tile_scroll_hint(true);
CHECK_MESSAGE(
test_container->is_scroll_hint_tiled(),
"Tiled scroll hint mode is enabled correctly.");
test_container->set_draw_focus_border(true);
CHECK_MESSAGE(
test_container->get_draw_focus_border(),
"draw_focus_border is enabled correctly.");
memdelete(test_control);
memdelete(test_container);
}
TEST_CASE("[SceneTree][ScrollContainer] ensure_control_visible and follow_focus") {
ScrollContainer *test_container = memnew(ScrollContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(test_container);
Control *content_control = memnew(Control);
content_control->set_custom_minimum_size(Size2(300, 300));
test_container->add_child(content_control);
Control *target_control = memnew(Control);
target_control->set_focus_mode(Control::FOCUS_ALL);
target_control->set_custom_minimum_size(Size2(20, 20));
target_control->set_position(Point2(260, 250));
target_control->set_size(Size2(20, 20));
content_control->add_child(target_control);
test_container->set_size(Size2(100, 100));
test_container->set_h_scroll(0);
test_container->set_v_scroll(0);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
test_container->get_h_scroll() == 0,
"Horizontal scroll starts at 0 for this setup.");
CHECK_MESSAGE(
test_container->get_v_scroll() == 0,
"Vertical scroll starts at 0 for this setup.");
test_container->ensure_control_visible(target_control);
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
test_container->get_h_scroll() > 0,
"ensure_control_visible() scrolls horizontally when target control is outside the visible area.");
CHECK_MESSAGE(
test_container->get_v_scroll() > 0,
"ensure_control_visible() scrolls vertically when target control is outside the visible area.");
Rect2 target_rect(target_control->get_position(), target_control->get_size());
real_t side_margin = test_container->get_v_scroll_bar()->is_visible() ? test_container->get_v_scroll_bar()->get_size().x : 0.0f;
real_t bottom_margin = test_container->get_h_scroll_bar()->is_visible() ? test_container->get_h_scroll_bar()->get_size().y : 0.0f;
real_t visible_left = test_container->get_h_scroll();
real_t visible_top = test_container->get_v_scroll();
real_t visible_right = visible_left + test_container->get_size().x - side_margin;
real_t visible_bottom = visible_top + test_container->get_size().y - bottom_margin;
CHECK_MESSAGE(
(target_rect.position.x >= visible_left && (target_rect.position.x + target_rect.size.x) <= visible_right),
"ensure_control_visible() fully reveals target control horizontally.");
CHECK_MESSAGE(
(target_rect.position.y >= visible_top && (target_rect.position.y + target_rect.size.y) <= visible_bottom),
"ensure_control_visible() fully reveals target control vertically.");
test_container->set_h_scroll(0);
test_container->set_v_scroll(0);
test_container->set_follow_focus(true);
SceneTree::get_singleton()->process(0);
target_control->grab_focus();
SceneTree::get_singleton()->process(0);
CHECK_MESSAGE(
test_container->get_h_scroll() > 0,
"follow_focus scrolls horizontally to focused descendants.");
CHECK_MESSAGE(
test_container->get_v_scroll() > 0,
"follow_focus scrolls vertically to focused descendants.");
visible_left = test_container->get_h_scroll();
visible_top = test_container->get_v_scroll();
visible_right = visible_left + test_container->get_size().x - side_margin;
visible_bottom = visible_top + test_container->get_size().y - bottom_margin;
CHECK_MESSAGE(
(target_rect.position.x >= visible_left && (target_rect.position.x + target_rect.size.x) <= visible_right),
"follow_focus fully reveals focused target control horizontally.");
CHECK_MESSAGE(
(target_rect.position.y >= visible_top && (target_rect.position.y + target_rect.size.y) <= visible_bottom),
"follow_focus fully reveals focused target control vertically.");
memdelete(target_control);
memdelete(content_control);
memdelete(test_container);
}
TEST_CASE("[SceneTree][ScrollContainer] Configuration warnings") {
ScrollContainer *test_container = memnew(ScrollContainer);
Window *root = SceneTree::get_singleton()->get_root();
root->add_child(test_container);
CHECK_MESSAGE(
test_container->get_configuration_warnings().size() == 1,
"ScrollContainer reports one configuration warning when it has no content child.");
Control *child_control_1 = memnew(Control);
Control *child_control_2 = memnew(Control);
test_container->add_child(child_control_1);
CHECK_MESSAGE(
test_container->get_configuration_warnings().is_empty(),
"ScrollContainer reports no configuration warning when it has exactly one content child.");
test_container->add_child(child_control_2);
CHECK_MESSAGE(
test_container->get_configuration_warnings().size() == 1,
"ScrollContainer reports one configuration warning when it has more than one content child.");
memdelete(child_control_2);
memdelete(child_control_1);
memdelete(test_container);
}
} // namespace TestScrollContainer