Repository: MyreMylar/pygame_gui
Pull request #539 (2024-03-10 19:51:38)

Add axis constraints to UIScrollingContainer
This is  a slightly different method to how it was done in the original PR.

Step 4(?) of splitting mega-PR #494 into smaller easier to review PRs


Patch:
diff --git a/pygame_gui/elements/ui_scrolling_container.py b/pygame_gui/elements/ui_scrolling_container.py
index b61de40..7ff254e 100644
--- a/pygame_gui/elements/ui_scrolling_container.py
+++ b/pygame_gui/elements/ui_scrolling_container.py
@@ -40,7 +40,10 @@ class UIScrollingContainer(UIElement, IContainerLikeInterface):
                  parent_element: Optional[UIElement] = None,
                  object_id: Optional[Union[ObjectID, str]] = None,
                  anchors: Optional[Dict[str, Union[str, UIElement]]] = None,
-                 visible: int = 1):
+                 visible: int = 1,
+                 allow_scroll_x: bool = True,
+                 allow_scroll_y: bool = True,
+                 ):
         # Need to move some declarations early as they are indirectly referenced via the ui element
         # constructor
         self._root_container = None
@@ -55,11 +58,14 @@ class UIScrollingContainer(UIElement, IContainerLikeInterface):
                          object_id=object_id,
                          element_id=['scrolling_container'])
 
-        # self.parent_element = parent_element
         self.scroll_bar_width = 0
         self.scroll_bar_height = 0
 
         self.need_to_sort_out_scrollbars = False
+
+        self.allow_scroll_x = allow_scroll_x
+        self.allow_scroll_y = allow_scroll_y
+
         self.vert_scroll_bar: Optional[UIVerticalScrollBar] = None
         self.horiz_scroll_bar: Optional[UIHorizontalScrollBar] = None
 
@@ -271,7 +277,11 @@ class UIScrollingContainer(UIElement, IContainerLikeInterface):
         bar has been moved. Instead, it tries to keep the scrollbars in the same approximate position
         they were in before resizing
         """
+
+        # First call to see if scrolling container size on its own necessitates scroll bars
         self._check_scroll_bars_and_adjust()
+        # second call to see if the view space contraction produced by any scroll bars created
+        # in the first call, require an additional scroll bar
         need_horiz_scroll_bar, need_vert_scroll_bar = self._check_scroll_bars_and_adjust()
 
         if need_vert_scroll_bar:
@@ -344,11 +354,11 @@ class UIScrollingContainer(UIElement, IContainerLikeInterface):
         need_horiz_scroll_bar = False
         need_vert_scroll_bar = False
         if (self.scrolling_height > self._view_container.rect.height or
-                self.scrollable_container.relative_rect.top != 0):
+                self.scrollable_container.relative_rect.top != 0) and self.allow_scroll_y:
             need_vert_scroll_bar = True
             self.scroll_bar_width = 20
         if (self.scrolling_width > self._view_container.rect.width or
-                self.scrollable_container.relative_rect.left != 0):
+                self.scrollable_container.relative_rect.left != 0) and self.allow_scroll_x:
             need_horiz_scroll_bar = True
             self.scroll_bar_height = 20
         if need_vert_scroll_bar or need_horiz_scroll_bar:
@@ -356,6 +366,16 @@ class UIScrollingContainer(UIElement, IContainerLikeInterface):
             new_height = (self._root_container.rect.height - self.scroll_bar_height)
             new_dimensions = (new_width, new_height)
             self._view_container.set_dimensions(new_dimensions)
+
+            if not self.allow_scroll_x:
+                # horizontal scrolling is banned, lets shrink the scrollable width
+                # to account for any scroll bar as well
+                self.scrollable_container.set_dimensions((new_width,
+                                                          self.scrollable_container.rect.height))
+            if not self.allow_scroll_y:
+                self.scrollable_container.set_dimensions((self.scrollable_container.rect.width,
+                                                          new_height))
+
         self._calculate_scrolling_dimensions()
         return need_horiz_scroll_bar, need_vert_scroll_bar
 


Test patch:
diff --git a/tests/test_elements/test_ui_scrolling_container.py b/tests/test_elements/test_ui_scrolling_container.py
index 0f07096..b68e11a 100644
--- a/tests/test_elements/test_ui_scrolling_container.py
+++ b/tests/test_elements/test_ui_scrolling_container.py
@@ -116,6 +116,43 @@ class TestUIScrollingContainer:
         assert container._view_container.rect.size == (200-container.vert_scroll_bar.rect.width,
                                                        200-container.horiz_scroll_bar.rect.height)
 
+    def test_set_scrollable_area_dimensions_constrained_x_axis(self, _init_pygame, default_ui_manager,
+                                                               _display_surface_return_none):
+        container = UIScrollingContainer(pygame.Rect(100, 100, 200, 200),
+                                         manager=default_ui_manager,
+                                         allow_scroll_x=False)
+
+        assert container.vert_scroll_bar is None
+        assert container.horiz_scroll_bar is None
+        assert container.scrollable_container.rect.size == (200, 200)
+
+        container.set_scrollable_area_dimensions((200, 600))
+
+        assert container.vert_scroll_bar is not None
+        assert container.horiz_scroll_bar is None
+        assert container._view_container.rect.size == (200 - container.vert_scroll_bar.rect.width,
+                                                       200)
+        assert container.scrollable_container.rect.size == (200 - container.vert_scroll_bar.rect.width, 600)
+
+    def test_set_scrollable_area_dimensions_constrained_y_axis(self, _init_pygame, default_ui_manager,
+                                                               _display_surface_return_none):
+        container = UIScrollingContainer(pygame.Rect(100, 100, 200, 200),
+                                         manager=default_ui_manager,
+                                         allow_scroll_y=False)
+
+        assert container.vert_scroll_bar is None
+        assert container.horiz_scroll_bar is None
+        assert container.scrollable_container.rect.size == (200, 200)
+
+        container.set_scrollable_area_dimensions((600, 200))
+
+        assert container.vert_scroll_bar is None
+        assert container.horiz_scroll_bar is not None
+        assert container._view_container.rect.size == (200,
+                                                       200 - container.horiz_scroll_bar.rect.height)
+        assert container.scrollable_container.rect.size == (600,
+                                                            200 - container.horiz_scroll_bar.rect.height)
+
     def test_update(self, _init_pygame, default_ui_manager,
                     _display_surface_return_none):
         container = UIScrollingContainer(pygame.Rect(100, 100, 200, 200),
@@ -128,7 +165,7 @@ class TestUIScrollingContainer:
 
         container.update(0.02)
 
-        assert container.get_container().relative_rect.x == -37
+        assert container.get_container().get_relative_rect().x == -37
 
         container.vert_scroll_bar.scroll_wheel_moved = True
         container.vert_scroll_bar.scroll_wheel_amount = -1.0
@@ -136,7 +173,7 @@ class TestUIScrollingContainer:
 
         container.update(0.02)
 
-        assert container.get_container().relative_rect.y == -67
+        assert container.get_container().get_relative_rect().y == -67
 
         container.horiz_scroll_bar.scroll_wheel_right = True
         container.horiz_scroll_bar.update(0.02)