Path: blob/master/tests/scene/test_flow_container.cpp
59209 views
/**************************************************************************/1/* test_flow_container.cpp */2/**************************************************************************/3/* This file is part of: */4/* GODOT ENGINE */5/* https://godotengine.org */6/**************************************************************************/7/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */8/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */9/* */10/* Permission is hereby granted, free of charge, to any person obtaining */11/* a copy of this software and associated documentation files (the */12/* "Software"), to deal in the Software without restriction, including */13/* without limitation the rights to use, copy, modify, merge, publish, */14/* distribute, sublicense, and/or sell copies of the Software, and to */15/* permit persons to whom the Software is furnished to do so, subject to */16/* the following conditions: */17/* */18/* The above copyright notice and this permission notice shall be */19/* included in all copies or substantial portions of the Software. */20/* */21/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */22/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */23/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */24/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */25/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */26/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */27/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */28/**************************************************************************/2930#include "tests/test_macros.h"3132TEST_FORCE_LINK(test_flow_container)3334#include "scene/gui/control.h"35#include "scene/gui/flow_container.h"36#include "scene/main/scene_tree.h"37#include "scene/main/window.h"3839namespace TestFlowContainer {4041TEST_CASE("[SceneTree][FlowContainer] HFlowContainer") {42HFlowContainer *hflow_container = memnew(HFlowContainer);43Window *root = SceneTree::get_singleton()->get_root();44root->add_child(hflow_container);4546hflow_container->add_theme_constant_override("h_separation", 0);47hflow_container->add_theme_constant_override("v_separation", 0);48hflow_container->set_size(Size2(100, 100));49SceneTree::get_singleton()->process(0);5051SUBCASE("Default behavior") {52Control *child_control_1 = memnew(Control);53Control *child_control_2 = memnew(Control);54Control *child_control_3 = memnew(Control);55child_control_1->set_custom_minimum_size(Size2(20, 10));56child_control_2->set_custom_minimum_size(Size2(20, 10));57child_control_3->set_custom_minimum_size(Size2(20, 10));58hflow_container->add_child(child_control_1);59hflow_container->add_child(child_control_2);60hflow_container->add_child(child_control_3);61SceneTree::get_singleton()->process(0);6263CHECK_MESSAGE(64hflow_container->get_alignment() == FlowContainer::ALIGNMENT_BEGIN,65"HFlowContainer alignment is set to ALIGNMENT_BEGIN by default.");6667CHECK_MESSAGE(68hflow_container->get_last_wrap_alignment() == FlowContainer::LAST_WRAP_ALIGNMENT_INHERIT,69"HFlowContainer last wrap alignment is set to LAST_WRAP_ALIGNMENT_INHERIT by default.");7071CHECK_MESSAGE(72!hflow_container->is_vertical(),73"HFlowContainer is horizontal.");7475CHECK_MESSAGE(76!hflow_container->is_reverse_fill(),77"HFlowContainer reverse fill is disabled by default.");7879CHECK_MESSAGE(80hflow_container->get_line_count() == 1,81"HFlowContainer keeps all children in one line when there is enough horizontal space.");8283CHECK_MESSAGE(84hflow_container->get_line_max_child_count() == 3,85"HFlowContainer reports the child count of the first line.");8687CHECK_MESSAGE(88child_control_1->get_position().is_equal_approx(Point2(0, 0)),89"First child control is positioned at the beginning of the line.");9091CHECK_MESSAGE(92child_control_2->get_position().is_equal_approx(Point2(20, 0)),93"Second child control is placed after the first child control.");9495CHECK_MESSAGE(96child_control_3->get_position().is_equal_approx(Point2(40, 0)),97"Third child control is placed after the second child control.");9899memdelete(child_control_3);100memdelete(child_control_2);101memdelete(child_control_1);102}103104SUBCASE("Wrapping and last wrap alignment") {105Control *child_control_1 = memnew(Control);106Control *child_control_2 = memnew(Control);107Control *child_control_3 = memnew(Control);108child_control_1->set_custom_minimum_size(Size2(20, 10));109child_control_2->set_custom_minimum_size(Size2(20, 10));110child_control_3->set_custom_minimum_size(Size2(20, 10));111hflow_container->set_size(Size2(50, 100));112hflow_container->add_child(child_control_1);113hflow_container->add_child(child_control_2);114hflow_container->add_child(child_control_3);115SceneTree::get_singleton()->process(0);116117CHECK_MESSAGE(118hflow_container->get_line_count() == 2,119"HFlowContainer wraps children to a second line when they no longer fit.");120121CHECK_MESSAGE(122hflow_container->get_line_max_child_count() == 2,123"HFlowContainer reports the first line child count as the line max child count.");124125CHECK_MESSAGE(126child_control_1->get_position().is_equal_approx(Point2(0, 0)),127"First child control starts at the first line origin.");128129CHECK_MESSAGE(130child_control_2->get_position().is_equal_approx(Point2(20, 0)),131"Second child control remains on the first line.");132133CHECK_MESSAGE(134child_control_3->get_position().is_equal_approx(Point2(0, 10)),135"Third child control wraps to the second line.");136137hflow_container->set_last_wrap_alignment(FlowContainer::LAST_WRAP_ALIGNMENT_CENTER);138SceneTree::get_singleton()->process(0);139140CHECK_MESSAGE(141child_control_3->get_position().is_equal_approx(Point2(10, 10)),142"Last wrapped line is centered relative to the previous line when LAST_WRAP_ALIGNMENT_CENTER is used.");143144hflow_container->set_last_wrap_alignment(FlowContainer::LAST_WRAP_ALIGNMENT_END);145SceneTree::get_singleton()->process(0);146147CHECK_MESSAGE(148child_control_3->get_position().is_equal_approx(Point2(20, 10)),149"Last wrapped line is aligned to the end relative to the previous line when LAST_WRAP_ALIGNMENT_END is used.");150151memdelete(child_control_3);152memdelete(child_control_2);153memdelete(child_control_1);154}155156SUBCASE("Alignment and RTL") {157Control *child_control_1 = memnew(Control);158Control *child_control_2 = memnew(Control);159child_control_1->set_custom_minimum_size(Size2(20, 10));160child_control_2->set_custom_minimum_size(Size2(20, 10));161hflow_container->set_size(Size2(100, 100));162hflow_container->add_child(child_control_1);163hflow_container->add_child(child_control_2);164SceneTree::get_singleton()->process(0);165166hflow_container->set_alignment(FlowContainer::ALIGNMENT_CENTER);167SceneTree::get_singleton()->process(0);168169CHECK_MESSAGE(170child_control_1->get_position().is_equal_approx(Point2(30, 0)),171"First child control is centered when ALIGNMENT_CENTER is used.");172173CHECK_MESSAGE(174child_control_2->get_position().is_equal_approx(Point2(50, 0)),175"Second child control follows centered alignment when ALIGNMENT_CENTER is used.");176177hflow_container->set_alignment(FlowContainer::ALIGNMENT_END);178SceneTree::get_singleton()->process(0);179180CHECK_MESSAGE(181child_control_1->get_position().is_equal_approx(Point2(60, 0)),182"First child control is aligned to the end when ALIGNMENT_END is used.");183184CHECK_MESSAGE(185child_control_2->get_position().is_equal_approx(Point2(80, 0)),186"Second child control is aligned to the end when ALIGNMENT_END is used.");187188hflow_container->set_alignment(FlowContainer::ALIGNMENT_BEGIN);189hflow_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);190SceneTree::get_singleton()->process(0);191192CHECK_MESSAGE(193child_control_1->get_position().is_equal_approx(Point2(80, 0)),194"First child control starts at the right edge in RTL mode with ALIGNMENT_BEGIN.");195196CHECK_MESSAGE(197child_control_2->get_position().is_equal_approx(Point2(60, 0)),198"Second child control follows right-to-left ordering in RTL mode with ALIGNMENT_BEGIN.");199200memdelete(child_control_2);201memdelete(child_control_1);202}203204SUBCASE("Expanding children and stretch ratio") {205Control *child_control_1 = memnew(Control);206Control *child_control_2 = memnew(Control);207child_control_1->set_custom_minimum_size(Size2(20, 10));208child_control_2->set_custom_minimum_size(Size2(20, 10));209child_control_1->set_h_size_flags(Control::SIZE_EXPAND_FILL);210child_control_2->set_h_size_flags(Control::SIZE_EXPAND_FILL);211hflow_container->set_size(Size2(100, 100));212hflow_container->add_child(child_control_1);213hflow_container->add_child(child_control_2);214SceneTree::get_singleton()->process(0);215216CHECK_MESSAGE(217child_control_1->get_size().is_equal_approx(Size2(50, 10)),218"First child control expands to half of the available width when both children have equal stretch ratio.");219220CHECK_MESSAGE(221child_control_2->get_size().is_equal_approx(Size2(50, 10)),222"Second child control expands to half of the available width when both children have equal stretch ratio.");223224CHECK_MESSAGE(225child_control_2->get_position().is_equal_approx(Point2(50, 0)),226"Second child control is placed immediately after the first expanded child control.");227228child_control_2->set_stretch_ratio(3.0f);229SceneTree::get_singleton()->process(0);230231CHECK_MESSAGE(232child_control_1->get_size().is_equal_approx(Size2(35, 10)),233"First child control receives less width when its stretch ratio is lower.");234235CHECK_MESSAGE(236child_control_2->get_size().is_equal_approx(Size2(65, 10)),237"Second child control receives more width when its stretch ratio is higher.");238239CHECK_MESSAGE(240child_control_2->get_position().is_equal_approx(Point2(35, 0)),241"Second child control starts after the first child control's stretched width.");242243memdelete(child_control_2);244memdelete(child_control_1);245}246247SUBCASE("Reverse fill") {248Control *child_control_1 = memnew(Control);249Control *child_control_2 = memnew(Control);250Control *child_control_3 = memnew(Control);251child_control_1->set_custom_minimum_size(Size2(20, 10));252child_control_2->set_custom_minimum_size(Size2(20, 10));253child_control_3->set_custom_minimum_size(Size2(20, 10));254hflow_container->set_size(Size2(50, 100));255hflow_container->add_child(child_control_1);256hflow_container->add_child(child_control_2);257hflow_container->add_child(child_control_3);258SceneTree::get_singleton()->process(0);259260CHECK_MESSAGE(261child_control_1->get_position().is_equal_approx(Point2(0, 0)),262"First child control starts at the top row by default.");263264CHECK_MESSAGE(265child_control_3->get_position().is_equal_approx(Point2(0, 10)),266"Third child control wraps to the next row by default.");267268hflow_container->set_reverse_fill(true);269SceneTree::get_singleton()->process(0);270271CHECK_MESSAGE(272child_control_1->get_position().is_equal_approx(Point2(0, 90)),273"First child control moves to the bottom row when reverse fill is enabled.");274275CHECK_MESSAGE(276child_control_2->get_position().is_equal_approx(Point2(20, 90)),277"Second child control remains in the same row as the first child control when reverse fill is enabled.");278279CHECK_MESSAGE(280child_control_3->get_position().is_equal_approx(Point2(0, 80)),281"Wrapped row order is inverted from bottom to top when reverse fill is enabled.");282283hflow_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);284SceneTree::get_singleton()->process(0);285286CHECK_MESSAGE(287child_control_1->get_position().is_equal_approx(Point2(30, 90)),288"RTL mirrors horizontal placement while reverse fill keeps rows bottom to top.");289290CHECK_MESSAGE(291child_control_2->get_position().is_equal_approx(Point2(10, 90)),292"Second child control follows mirrored RTL order while reverse fill remains enabled.");293294CHECK_MESSAGE(295child_control_3->get_position().is_equal_approx(Point2(30, 80)),296"Wrapped child control is mirrored in RTL while keeping reverse-filled row order.");297298memdelete(child_control_3);299memdelete(child_control_2);300memdelete(child_control_1);301}302303memdelete(hflow_container);304}305306TEST_CASE("[SceneTree][FlowContainer] VFlowContainer") {307VFlowContainer *vflow_container = memnew(VFlowContainer);308Window *root = SceneTree::get_singleton()->get_root();309root->add_child(vflow_container);310311vflow_container->add_theme_constant_override("h_separation", 0);312vflow_container->add_theme_constant_override("v_separation", 0);313vflow_container->set_size(Size2(100, 100));314SceneTree::get_singleton()->process(0);315316SUBCASE("Default behavior") {317Control *child_control_1 = memnew(Control);318Control *child_control_2 = memnew(Control);319Control *child_control_3 = memnew(Control);320child_control_1->set_custom_minimum_size(Size2(20, 10));321child_control_2->set_custom_minimum_size(Size2(20, 10));322child_control_3->set_custom_minimum_size(Size2(20, 10));323vflow_container->add_child(child_control_1);324vflow_container->add_child(child_control_2);325vflow_container->add_child(child_control_3);326SceneTree::get_singleton()->process(0);327328CHECK_MESSAGE(329vflow_container->get_alignment() == FlowContainer::ALIGNMENT_BEGIN,330"VFlowContainer alignment is set to ALIGNMENT_BEGIN by default.");331332CHECK_MESSAGE(333vflow_container->get_last_wrap_alignment() == FlowContainer::LAST_WRAP_ALIGNMENT_INHERIT,334"VFlowContainer last wrap alignment is set to LAST_WRAP_ALIGNMENT_INHERIT by default.");335336CHECK_MESSAGE(337vflow_container->is_vertical(),338"VFlowContainer is vertical.");339340CHECK_MESSAGE(341!vflow_container->is_reverse_fill(),342"VFlowContainer reverse fill is disabled by default.");343344CHECK_MESSAGE(345vflow_container->get_line_count() == 1,346"VFlowContainer keeps all children in one column when there is enough vertical space.");347348CHECK_MESSAGE(349vflow_container->get_line_max_child_count() == 3,350"VFlowContainer reports the child count of the first column.");351352CHECK_MESSAGE(353child_control_1->get_position().is_equal_approx(Point2(0, 0)),354"First child control is positioned at the top of the first column.");355356CHECK_MESSAGE(357child_control_2->get_position().is_equal_approx(Point2(0, 10)),358"Second child control is placed below the first child control.");359360CHECK_MESSAGE(361child_control_3->get_position().is_equal_approx(Point2(0, 20)),362"Third child control is placed below the second child control.");363364memdelete(child_control_3);365memdelete(child_control_2);366memdelete(child_control_1);367}368369SUBCASE("Wrapping and last wrap alignment") {370Control *child_control_1 = memnew(Control);371Control *child_control_2 = memnew(Control);372Control *child_control_3 = memnew(Control);373child_control_1->set_custom_minimum_size(Size2(20, 20));374child_control_2->set_custom_minimum_size(Size2(20, 20));375child_control_3->set_custom_minimum_size(Size2(20, 20));376vflow_container->set_size(Size2(100, 50));377vflow_container->add_child(child_control_1);378vflow_container->add_child(child_control_2);379vflow_container->add_child(child_control_3);380SceneTree::get_singleton()->process(0);381382CHECK_MESSAGE(383vflow_container->get_line_count() == 2,384"VFlowContainer wraps children to a second column when they no longer fit vertically.");385386CHECK_MESSAGE(387vflow_container->get_line_max_child_count() == 2,388"VFlowContainer reports the first column child count as the line max child count.");389390CHECK_MESSAGE(391child_control_1->get_position().is_equal_approx(Point2(0, 0)),392"First child control starts at the first column origin.");393394CHECK_MESSAGE(395child_control_2->get_position().is_equal_approx(Point2(0, 20)),396"Second child control remains in the first column.");397398CHECK_MESSAGE(399child_control_3->get_position().is_equal_approx(Point2(20, 0)),400"Third child control wraps to the second column.");401402vflow_container->set_last_wrap_alignment(FlowContainer::LAST_WRAP_ALIGNMENT_CENTER);403SceneTree::get_singleton()->process(0);404405CHECK_MESSAGE(406child_control_3->get_position().is_equal_approx(Point2(20, 10)),407"Last wrapped column is centered relative to the previous column when LAST_WRAP_ALIGNMENT_CENTER is used.");408409vflow_container->set_last_wrap_alignment(FlowContainer::LAST_WRAP_ALIGNMENT_END);410SceneTree::get_singleton()->process(0);411412CHECK_MESSAGE(413child_control_3->get_position().is_equal_approx(Point2(20, 20)),414"Last wrapped column is aligned to the end relative to the previous column when LAST_WRAP_ALIGNMENT_END is used.");415416memdelete(child_control_3);417memdelete(child_control_2);418memdelete(child_control_1);419}420421SUBCASE("Alignment and RTL") {422Control *child_control_1 = memnew(Control);423Control *child_control_2 = memnew(Control);424child_control_1->set_custom_minimum_size(Size2(20, 20));425child_control_2->set_custom_minimum_size(Size2(20, 20));426vflow_container->set_size(Size2(100, 100));427vflow_container->add_child(child_control_1);428vflow_container->add_child(child_control_2);429SceneTree::get_singleton()->process(0);430431vflow_container->set_alignment(FlowContainer::ALIGNMENT_CENTER);432SceneTree::get_singleton()->process(0);433434CHECK_MESSAGE(435child_control_1->get_position().is_equal_approx(Point2(0, 30)),436"First child control is vertically centered when ALIGNMENT_CENTER is used.");437438CHECK_MESSAGE(439child_control_2->get_position().is_equal_approx(Point2(0, 50)),440"Second child control follows centered vertical alignment when ALIGNMENT_CENTER is used.");441442vflow_container->set_alignment(FlowContainer::ALIGNMENT_END);443SceneTree::get_singleton()->process(0);444445CHECK_MESSAGE(446child_control_1->get_position().is_equal_approx(Point2(0, 60)),447"First child control is aligned to the bottom when ALIGNMENT_END is used.");448449CHECK_MESSAGE(450child_control_2->get_position().is_equal_approx(Point2(0, 80)),451"Second child control is aligned to the bottom when ALIGNMENT_END is used.");452453vflow_container->set_alignment(FlowContainer::ALIGNMENT_BEGIN);454vflow_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);455SceneTree::get_singleton()->process(0);456457CHECK_MESSAGE(458child_control_1->get_position().is_equal_approx(Point2(80, 0)),459"First child control moves to the right edge in RTL mode with vertical flow.");460461CHECK_MESSAGE(462child_control_2->get_position().is_equal_approx(Point2(80, 20)),463"Second child control follows RTL horizontal mirroring in vertical flow.");464465memdelete(child_control_2);466memdelete(child_control_1);467}468469SUBCASE("Expanding children and stretch ratio") {470Control *child_control_1 = memnew(Control);471Control *child_control_2 = memnew(Control);472child_control_1->set_custom_minimum_size(Size2(20, 20));473child_control_2->set_custom_minimum_size(Size2(20, 20));474child_control_1->set_v_size_flags(Control::SIZE_EXPAND_FILL);475child_control_2->set_v_size_flags(Control::SIZE_EXPAND_FILL);476vflow_container->set_size(Size2(100, 100));477vflow_container->add_child(child_control_1);478vflow_container->add_child(child_control_2);479SceneTree::get_singleton()->process(0);480481CHECK_MESSAGE(482child_control_1->get_size().is_equal_approx(Size2(20, 50)),483"First child control expands to half of the available height when both children have equal stretch ratio.");484485CHECK_MESSAGE(486child_control_2->get_size().is_equal_approx(Size2(20, 50)),487"Second child control expands to half of the available height when both children have equal stretch ratio.");488489CHECK_MESSAGE(490child_control_2->get_position().is_equal_approx(Point2(0, 50)),491"Second child control is placed immediately after the first expanded child control.");492493child_control_2->set_stretch_ratio(3.0f);494SceneTree::get_singleton()->process(0);495496CHECK_MESSAGE(497child_control_1->get_size().is_equal_approx(Size2(20, 35)),498"First child control receives less height when its stretch ratio is lower.");499500CHECK_MESSAGE(501child_control_2->get_size().is_equal_approx(Size2(20, 65)),502"Second child control receives more height when its stretch ratio is higher.");503504CHECK_MESSAGE(505child_control_2->get_position().is_equal_approx(Point2(0, 35)),506"Second child control starts after the first child control's stretched height.");507508memdelete(child_control_2);509memdelete(child_control_1);510}511512SUBCASE("Reverse fill") {513Control *child_control_1 = memnew(Control);514Control *child_control_2 = memnew(Control);515Control *child_control_3 = memnew(Control);516child_control_1->set_custom_minimum_size(Size2(20, 20));517child_control_2->set_custom_minimum_size(Size2(20, 20));518child_control_3->set_custom_minimum_size(Size2(20, 20));519vflow_container->set_size(Size2(100, 30));520vflow_container->add_child(child_control_1);521vflow_container->add_child(child_control_2);522vflow_container->add_child(child_control_3);523SceneTree::get_singleton()->process(0);524525CHECK_MESSAGE(526child_control_1->get_position().is_equal_approx(Point2(0, 0)),527"First child control starts in the first column by default.");528529CHECK_MESSAGE(530child_control_2->get_position().is_equal_approx(Point2(20, 0)),531"Second child control starts in the second column by default.");532533CHECK_MESSAGE(534child_control_3->get_position().is_equal_approx(Point2(40, 0)),535"Third child control starts in the third column by default.");536537vflow_container->set_reverse_fill(true);538SceneTree::get_singleton()->process(0);539540CHECK_MESSAGE(541child_control_1->get_position().is_equal_approx(Point2(80, 0)),542"First child control starts from the right when reverse fill is enabled in vertical mode.");543544CHECK_MESSAGE(545child_control_2->get_position().is_equal_approx(Point2(60, 0)),546"Second child control follows right-to-left column filling when reverse fill is enabled.");547548CHECK_MESSAGE(549child_control_3->get_position().is_equal_approx(Point2(40, 0)),550"Third child control continues right-to-left column filling when reverse fill is enabled.");551552vflow_container->set_layout_direction(Control::LAYOUT_DIRECTION_RTL);553SceneTree::get_singleton()->process(0);554555CHECK_MESSAGE(556child_control_1->get_position().is_equal_approx(Point2(0, 0)),557"Vertical flow with RTL and reverse fill fills columns from left to right.");558559CHECK_MESSAGE(560child_control_2->get_position().is_equal_approx(Point2(20, 0)),561"Second child control follows left-to-right order in vertical mode with RTL and reverse fill.");562563CHECK_MESSAGE(564child_control_3->get_position().is_equal_approx(Point2(40, 0)),565"Third child control follows left-to-right order in vertical mode with RTL and reverse fill.");566567memdelete(child_control_3);568memdelete(child_control_2);569memdelete(child_control_1);570}571572memdelete(vflow_container);573}574575} // namespace TestFlowContainer576577578