/**************************************************************************/ /* 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