Repository: MyreMylar/pygame_gui (python)

Issue:
Mouse event handling is in drawing Z order when it should be reversed?
**Describe the bug**

Given two overlapping elements, the bottom element gets precedence for mouse press events, when the topmost element should.

**To Reproduce**
Steps to reproduce the behaviour:
1. Create two overlapping buttons.
2. Click the visible button
3. See that the button underneath gets mouse events, and reports click events.

**Expected behaviour**
Drawing should be back to front (as it is). Event handling should be front to back.

**Screenshots**

N/A

**Platform and software (please complete the following information):**
 - OS: Linux
 - Pygame GUI version - latest
 - Pygame version - latest

**Additional context**

Code to reproduce the issue. Prints button click events to stdout.

```python
import pygame
import pygame_gui
from pygame_gui import UIManager
from pygame_gui.elements import UIButton

def main():
    pygame.init()
    resolution = (800, 480)
    btn_rect1 = pygame.Rect((100, 100), (200, 100))
    btn_rect2 = pygame.Rect((200, 100), (200, 100))
    background_surface = pygame.Surface(resolution)
    window_surface = pygame.display.set_mode(resolution,
                            pygame.FULLSCREEN)
    clock = pygame.time.Clock()
    ui_manager = UIManager(resolution)
    background_surface.fill(ui_manager.get_theme().get_colour('dark_bg'))

    btn1 = UIButton(btn_rect1,
                    "Thief",
                    ui_manager,
                    object_id='thief')

    btn2 = UIButton(btn_rect2,
                    "Click me",
                    ui_manager,
                    object_id='visible')

    while True:
        time_delta = clock.tick(30) / 1000.0
        for event in pygame.event.get():
            ui_manager.process_events(event)
            if event.type == pygame_gui.UI_BUTTON_PRESSED:
                print(f'Pressed button {event.ui_element.text}')
        ui_manager.update(time_delta)
        window_surface.blit(background_surface, (0, 0))
        ui_manager.draw_ui(window_surface)
        pygame.display.update()


main()
```


Pull request:
Fix event handling order of unlayered elements
fixes #609

Patch:
diff --git a/pygame_gui/core/ui_appearance_theme.py b/pygame_gui/core/ui_appearance_theme.py
index 2fe3d84..700e0bb 100644
--- a/pygame_gui/core/ui_appearance_theme.py
+++ b/pygame_gui/core/ui_appearance_theme.py
@@ -103,7 +103,7 @@ class UIAppearanceTheme(IUIAppearanceThemeInterface):
             self.need_to_rebuild_data_manually_changed = False
             return True
         return False
-
+    
     @staticmethod
     def _json_to_dict(json_data):
         if isinstance(json_data, dict):
@@ -574,19 +574,16 @@ class UIAppearanceTheme(IUIAppearanceThemeInterface):
 
         :return IGUIFontInterface: An interface to a pygame font object wrapper.
         """
-        font = None
-        
+        # set the default font as the final fall back
+        font = self.font_dict.get_default_font()
+
         for combined_element_id in combined_element_ids:
             if combined_element_id in self.ele_font_res:
                 if self._locale in self.ele_font_res[combined_element_id]:
-                    font = self.ele_font_res[combined_element_id][self._locale].loaded_font
+                    return self.ele_font_res[combined_element_id][self._locale].loaded_font
                 else:
-                    font = self.ele_font_res[combined_element_id]['en'].loaded_font
-                break
+                    return self.ele_font_res[combined_element_id]['en'].loaded_font
 
-        # set the default font as the final fall back
-        if font is None:
-            font = self.font_dict.get_default_font()
         return font
 
     def get_misc_data(self, misc_data_id: str, combined_element_ids: List[str]) -> Union[str, Dict]:
@@ -770,7 +767,7 @@ class UIAppearanceTheme(IUIAppearanceThemeInterface):
                     self._load_element_font_data_from_theme(file_dict,
                                                             element_name)
 
-            if data_type == 'colours' or data_type == 'colors':
+            if data_type == 'colours':
                 self._load_element_colour_data_from_theme(data_type,
                                                           element_name,
                                                           element_theming)
@@ -1048,7 +1045,7 @@ class UIAppearanceTheme(IUIAppearanceThemeInterface):
         :param theme_dict: The data dictionary from the theming file to load data from.
         """
         for data_type in theme_dict['defaults']:
-            if data_type == 'colours' or data_type == 'colors':
+            if data_type == 'colours':
                 colours_dict = theme_dict['defaults'][data_type]
                 for colour_key in colours_dict:
                     colour = self._load_colour_or_gradient_from_theme(colours_dict,
diff --git a/pygame_gui/elements/ui_image.py b/pygame_gui/elements/ui_image.py
index 22abbf4..232893e 100644
--- a/pygame_gui/elements/ui_image.py
+++ b/pygame_gui/elements/ui_image.py
@@ -37,8 +37,7 @@ class UIImage(UIElement):
                  anchors: Optional[Dict[str, Union[str, UIElement]]] = None,
                  visible: int = 1,
                  *,
-                 starting_height: int = 1,
-                 scale_func=pygame.transform.smoothscale):
+                 starting_height: int = 1,):
 
         super().__init__(relative_rect, manager, container,
                          starting_height=starting_height,
@@ -51,7 +50,7 @@ class UIImage(UIElement):
 
         self.original_image = None
 
-        self.set_image(image_surface, image_is_alpha_premultiplied, scale_func)
+        self.set_image(image_surface, image_is_alpha_premultiplied)
         self.rebuild_from_changed_theme_data()
 
     def rebuild_from_changed_theme_data(self):
@@ -80,8 +79,7 @@ class UIImage(UIElement):
 
     def set_image(self,
                   new_image: Union[pygame.surface.Surface, None],
-                  image_is_alpha_premultiplied: bool = False,
-                  scale_func = pygame.transform.smoothscale) -> None:
+                  image_is_alpha_premultiplied: bool = False) -> None:
         """
         Allows users to change the image displayed on a UIImage element during run time, without recreating
         the element.
@@ -99,6 +97,6 @@ class UIImage(UIElement):
         if (image_surface.get_width() != self.rect.width or
                 image_surface.get_height() != self.rect.height):
             self.original_image = image_surface
-            self._set_image(scale_func(self.original_image, self.rect.size))
+            self._set_image(pygame.transform.smoothscale(self.original_image, self.rect.size))
         else:
             self._set_image(image_surface)
diff --git a/pygame_gui/ui_manager.py b/pygame_gui/ui_manager.py
index 08802af..116d832 100644
--- a/pygame_gui/ui_manager.py
+++ b/pygame_gui/ui_manager.py
@@ -222,6 +222,7 @@ class UIManager(IUIManagerInterface):
         sorted_layers = sorted(self.ui_group.layers(), reverse=True)
         for layer in sorted_layers:
             sprites_in_layer = self.ui_group.get_sprites_from_layer(layer)
+            sprites_in_layer.reverse()
             if not sorting_consumed_event:
                 windows_in_layer = [window for window in sprites_in_layer
                                     if 'window' in window.element_ids[-1]]
@@ -336,7 +337,9 @@ class UIManager(IUIManagerInterface):
         hover_handled = False
         sorted_layers = sorted(self.ui_group.layers(), reverse=True)
         for layer in sorted_layers:
-            for ui_element in self.ui_group.get_sprites_from_layer(layer):
+            layer_elements = self.ui_group.get_sprites_from_layer(layer)
+            layer_elements.reverse()
+            for ui_element in layer_elements:
                 if ui_element.visible:
                     # Only check hover for visible elements - ignore hidden elements
                     # we need to check hover even after already found what we are hovering,


Test patch:
diff --git a/tests/test_core/test_ui_appearance_theme.py b/tests/test_core/test_ui_appearance_theme.py
index 3920cbe..2d2c206 100644
--- a/tests/test_core/test_ui_appearance_theme.py
+++ b/tests/test_core/test_ui_appearance_theme.py
@@ -192,11 +192,8 @@ class TestUIAppearanceTheme:
         assert shadow_width == '0'
 
     def test_get_font(self, _init_pygame, _display_surface_return_none: None):
-        resource_loader = BlockingThreadedResourceLoader()
-        theme = UIAppearanceTheme(resource_loader, locale='nonsense_locale')
+        theme = UIAppearanceTheme(BlockingThreadedResourceLoader(), locale='nonsense_locale')
         theme.load_theme(os.path.join("tests", "data", "themes", "ui_button_non_default.json"))
-        resource_loader.start()
-        resource_loader.update()
         assert theme.get_font_info(['button']) == theme.ui_element_fonts_info['button']['en']
         assert theme.get_font(['button']) == theme.ele_font_res['button']['en'].loaded_font
 
diff --git a/tests/test_elements/test_ui_scrolling_container.py b/tests/test_elements/test_ui_scrolling_container.py
index fd612c6..b882243 100644
--- a/tests/test_elements/test_ui_scrolling_container.py
+++ b/tests/test_elements/test_ui_scrolling_container.py
@@ -438,13 +438,13 @@ class TestUIScrollingContainer:
         assert container.are_contents_hovered()
         assert container.vert_scroll_bar is not None
 
-        container.vert_scroll_bar.process_event(pygame.event.Event(pygame.MOUSEWHEEL, {'y': -0.5}))
+        container.vert_scroll_bar.process_event(pygame.event.Event(pygame.MOUSEWHEEL, {'y': -0.5, 'x': 0.0}))
 
         assert container.vert_scroll_bar.scroll_wheel_moved
 
         assert container.horiz_scroll_bar is not None
 
-        container.horiz_scroll_bar.process_event(pygame.event.Event(pygame.MOUSEWHEEL, {'x': -0.5}))
+        container.horiz_scroll_bar.process_event(pygame.event.Event(pygame.MOUSEWHEEL, {'x': -0.5, 'y': 0.0}))
 
         assert container.horiz_scroll_bar.scroll_wheel_moved
 
@@ -465,7 +465,7 @@ class TestUIScrollingContainer:
         assert container.are_contents_hovered()
         assert container.vert_scroll_bar is not None
 
-        manager.process_events(pygame.event.Event(pygame.MOUSEWHEEL, {'y': -0.5}))
+        manager.process_events(pygame.event.Event(pygame.MOUSEWHEEL, {'y': -0.5, 'x': 0.0}))
 
         assert container.vert_scroll_bar.scroll_wheel_moved
 
@@ -490,7 +490,7 @@ class TestUIScrollingContainer:
         assert container.are_contents_hovered()
         assert container.vert_scroll_bar is not None
 
-        manager.process_events(pygame.event.Event(pygame.MOUSEWHEEL, {'y': -0.5}))
+        manager.process_events(pygame.event.Event(pygame.MOUSEWHEEL, {'y': -0.5, 'x': 0.0}))
 
         assert not container.vert_scroll_bar.scroll_wheel_moved
 
@@ -513,7 +513,7 @@ class TestUIScrollingContainer:
         assert container.are_contents_hovered()
         assert container.vert_scroll_bar is not None
 
-        manager.process_events(pygame.event.Event(pygame.MOUSEWHEEL, {'y': -0.5}))
+        manager.process_events(pygame.event.Event(pygame.MOUSEWHEEL, {'y': -0.5, 'x': 0.0}))
 
         assert container.vert_scroll_bar.scroll_wheel_moved