2
$\begingroup$

The following modal operator (after launched from F3) should report the type of left-clicked screen area.

*But instead of reporting the type of clicked area - it reports the type of area from which it was initially started:


from bpy.types import Operator
from bpy.utils import register_class


class Get_Area_Type(Operator):
    """Report the type of area beneath the mouse cursor"""

    bl_idname = "screen.get_area_type"
    bl_label = "Get Area Type"
    bl_options = set()

    def modal(self,context,event):
        if event.type in ('RIGHTMOUSE','ESC'):
            return self.cancel(context)
        if event.type == 'LEFTMOUSE':
            self.report({'INFO'}, context.area.type)
        return {'RUNNING_MODAL'}

    def invoke(self,context,event):
        context.window_manager.modal_handler_add(self)
        context.window.cursor_set("EYEDROPPER")
        return {'RUNNING_MODAL'}

    def cancel(self,context):
        self.report({'INFO'}, 'Operator Stopped')
        return {'CANCELLED'}


register_class(Get_Area_Type)

E.g., when we launch operator from 3D Viewport and click on Outliner:

  • it should report 'OUTLINER'
  • but instead it reports 'VIEW_3D' (the type of area from which it was launched)

How to fix this problem?


$\endgroup$

1 Answer 1

2
$\begingroup$

try this:

import bpy
from bpy.types import Operator
from bpy.utils import register_class


class Get_Area_Type(Operator):
    """Report the type of area beneath the mouse cursor"""
    bl_idname = "screen.get_area_type"
    bl_label = "Get Area Type"
    bl_options = set()

    def modal(self, context, event):
        if event.type in {'RIGHTMOUSE', 'ESC'}:
            return self.cancel(context)
        if event.type == 'LEFTMOUSE':
            mouse_x, mouse_y = event.mouse_x, event.mouse_y

            # Loop through all areas in the current screen
            for area in context.window.screen.areas:
                if (area.x <= mouse_x < area.x + area.width and
                    area.y <= mouse_y < area.y + area.height):

                    self.report({'INFO'}, f"Area under mouse: {area.type}")
                    break
    
        return {'RUNNING_MODAL'}

    def invoke(self, context, event):
        context.window_manager.modal_handler_add(self)
        context.window.cursor_modal_set("EYEDROPPER")
        return {'RUNNING_MODAL'}

    def cancel(self, context):
        self.report({'INFO'}, 'Operator Stopped')
        return {'CANCELLED'}


register_class(Get_Area_Type)

In Blender, context.area inside your modal operator never automatically updates to the area under the mouse. It always stays bound to the area where the operator started. So if you want the real area the mouse is hovering over, you can’t rely on context.area at all — you have to manually detect it every time in modal() using the absolute mouse coordinates (event.mouse_x, event.mouse_y).

$\endgroup$
2
  • 1
    $\begingroup$ HUGE THANKS for the working solution and for explaining how context works for modal operators. $\endgroup$ Commented Aug 8 at 16:42
  • 1
    $\begingroup$ you are welcome $\endgroup$ Commented Aug 9 at 7:48

You must log in to answer this question.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.