Exemplo n.º 1
0
    def __init__(self, state):
        u""" Initialize ScreenMouse"""
        if __debug__: print 'ScreenMouse __init__'
        self._init_done = False
        self._sprites = [] # sprite stack
        super(ScreenMouse, self).__init__()
        self._worlds = SortedList([], lambda x: -x.layer)
        if __debug__: print 'ScreenMouse __init__ _worlds SortedList done'
        self._init_done = True
        #-- public --#
        self.screen_pos = Vec3(0, 0)
        self.layer = 100000
        self.collision_rect_offset = Vec3(0, 0)
        self.hover_entity = None
        self.hover_renderer = None
        #-- unsure --#
        # TODO: add default sprite
#        self.position = None
#        self._capturer = None
#        Not sure if this is right place
#        self.on_screenmouse_button_down = Signal('screenmouse_button_down')
#        self.on_screenmouse_button_up = Signal('screenmouse_button_up')
        self.is_dragging = False
        self.events = pyknic.events.SignalProvider()
        if __debug__: print 'ScreenMouse __init__ SignalProvider done'
        self._state = state
        self.register_event_listeners()
        if __debug__: print 'ScreenMouse __init__ register events done'
        if __debug__: print 'ScreenMouse __init__ done'
Exemplo n.º 2
0
class ScreenMouse(pyknic.entity.Entity):
    u"""
    The pyknic mouse. 
    
    Features:
     * hot-spot
     * sprite stack
     * capture
     * automatic send of enter/leave an entity and/or renderer
    
    The hot-spot of the mouse is actually defined by the offset of the sprite 
    (or look at it as the cursor sprite is adjusted such the hot-spot is at the 
    coords or the mouse).
    
    :Ivariables:
        screen_pos : Vec3
            the current screen pos
        layer : int
            the layer the mouse is in
        collision_rect_offset : Vec3
            offset of the self.rect from the hot-spot
            self.rect is used for collision checking, so be careful using this option
        rect : Rect
            should always have (0, 0) as size, unless wanted differently, actually 
            it is not direcly used by ScreenMouse, but later using collision detection
            it will be useful. Its like the bounding box of the mouse entity.
        hover_entity : entity
            the topmost entity the mouse is currently over (that returns hit() == True)
        hover_renderer : renderer
            the topmost renderer the mouse is currently over (that returns hit() == True)
    
    
    :TODO:
        Should mouse also send on_gui_mouse_press /release to entites?
    """

    def __init__(self, state):
        u""" Initialize ScreenMouse"""
        if __debug__: print 'ScreenMouse __init__'
        self._init_done = False
        self._sprites = [] # sprite stack
        super(ScreenMouse, self).__init__()
        self._worlds = SortedList([], lambda x: -x.layer)
        if __debug__: print 'ScreenMouse __init__ _worlds SortedList done'
        self._init_done = True
        #-- public --#
        self.screen_pos = Vec3(0, 0)
        self.layer = 100000
        self.collision_rect_offset = Vec3(0, 0)
        self.hover_entity = None
        self.hover_renderer = None
        #-- unsure --#
        # TODO: add default sprite
#        self.position = None
#        self._capturer = None
#        Not sure if this is right place
#        self.on_screenmouse_button_down = Signal('screenmouse_button_down')
#        self.on_screenmouse_button_up = Signal('screenmouse_button_up')
        self.is_dragging = False
        self.events = pyknic.events.SignalProvider()
        if __debug__: print 'ScreenMouse __init__ SignalProvider done'
        self._state = state
        self.register_event_listeners()
        if __debug__: print 'ScreenMouse __init__ register events done'
        if __debug__: print 'ScreenMouse __init__ done'

    def register_event_listeners(self):
        state_events = self._state.events
        # unregister
        state_events.mouse_drag -= self.on_mouse_drag
        state_events.mouse_drag_drop -= self.on_drag_drop
        state_events.mouse_drag_start -= self.on_drag_start
        state_events.mouse_motion -= self.on_mouse_motion
        state_events.mouse_button_down -= self.on_mouse_button_down
        state_events.mouse_button_up -= self.on_mouse_button_up
        # register
        state_events.mouse_drag += self.on_mouse_drag
        state_events.mouse_drag_drop += self.on_drag_drop
        state_events.mouse_drag_start += self.on_drag_start
        state_events.mouse_motion += self.on_mouse_motion
        state_events.mouse_button_down += self.on_mouse_button_down
        state_events.mouse_button_up += self.on_mouse_button_up

#    is_captured = property(lambda self: self._capturer is not None, doc=u'read only, return bool')
#    def _set_capturer(self, capturer):
#        self._capturer = capturer
#    capturer = property(lambda self: self._capturer, _set_capturer, doc=u'get/set capturer, set to None to release capture')

    def _set_spr(self, value):
        # allow to set a sprite from __init__
        if not self._init_done:
            self.push_sprite(value)
        else:
            raise Exception(u'spr is read only for mouse, use push_sprite() and pop_sprite() instead!')
    spr = property(lambda self: self._sprites[-1], _set_spr, doc=u'read-only, use push_sprite() and pop_sprite() instead!')

    def on_mouse_drag(self, pos, rel, buttons, mods):
        u"""
        :TODO: doc
        """
        self.is_dragging = True
        self.on_mouse_motion(pos, rel, True)
        if self.hover_renderer:
            self.events.screenmouse_drag(self.position, self.hover_renderer.screen_to_world(Vec3(*rel)), buttons, mods)

    def on_drag_drop(self, pos, buttons, mods):
        if self.hover_renderer:
            self.events.screenmouse_drag_drop(self.position, buttons, mods)
            if self.hover_entity:
                self.hover_entity.on_screenmouse_drag_drop(self.position, buttons, mods)

    def on_drag_start(self, pos, buttons, mods):
        if self.hover_renderer:
            self.events.screenmouse_drag_start(self.position, buttons, mods)
            if self.hover_entity:
                self.hover_entity.on_screenmouse_drag_start(self.position, buttons, mods)

    def on_mouse_motion(self, pos, rel, dragging=False):
        u"""
        :TODO: doc
        """
        self.is_dragging = dragging
        self.screen_pos = Vec3(*pos)
        # TODO: is this a good idea to stop processing if captured?
        #if not self.is_captured:
        # check world collisions
        for world in self._worlds: # normally one world
            for renderer in world.get_renderers(): # normally 1-2 renderers
                if renderer.hit(self.screen_pos): # normally only 1 is hit
                    if renderer != self.hover_renderer:
                        if self.hover_renderer:
                            self.hover_renderer.on_screenmouse_leave(self.screen_pos, dragging)
                        self.hover_renderer = renderer
                        renderer.on_screenmouse_enter(self.screen_pos, dragging)
                    # update world position
                    self.position = renderer.screen_to_world(self.screen_pos)
                    if self.position:
                        self.rect.topleft = self.position.as_xy_tuple()
                        # entites sorted by layer, last is in front
                        for entity in reversed(world.get_entities_in_region(self.rect)):
                            # only do something if we hit an entity different from last time
                            if entity.hit(self.position):
                                # check if entity is hit
                                if entity != self and entity != self.hover_entity:
                                    # replace
                                    if self.hover_entity: # may be None
                                        self.hover_entity.on_screenmouse_leave(self.position, dragging)
                                    self.hover_entity = entity
                                    entity.on_screenmouse_enter(self.position, dragging)
                                return
                            else:
                                if self.hover_entity:
                                    self.hover_entity.on_screenmouse_leave(self.position, dragging)
                                    self.hover_entity = None
                        self.events.screenmouse_motion(self.position, self.hover_renderer.screen_to_world(Vec3(*rel)))
                    else:
                        # FIXME: TODO: this isnt a good nor elegant solution!!
                        raise Exception( 'SCREENMOUSE invalid POSITON %s' % (self.position))
                        self.position = Vec3(-99999, -99999, -99999)
                    return
            # either no world or no entity.hit() return True
            if self.hover_entity:
                self.hover_entity.on_screenmouse_leave(self.position, self.is_dragging)
                self.hover_entity = None
        if self.hover_renderer:
            self.hover_renderer.on_screenmouse_leave(self.screen_pos, self.is_dragging)
            self.hover_renderer = None

    def on_mouse_button_down(self, pos, buttons, mods):
        if self.hover_renderer:
            if self.hover_entity:
                self.hover_entity.on_screenmouse_button_down(self.position, buttons, mods)
            self.events.screenmouse_button_down(self.position, buttons, mods)

    def on_mouse_button_up(self, pos, buttons, mods):
        if self.hover_renderer:
            if self.hover_entity:
                self.hover_entity.on_screenmouse_button_up(self.position, buttons, mods)
            self.events.screenmouse_button_up(self.position, buttons, mods)

    def render(self, screen_surf, offset=Vec3(0, 0), screen_offset=Vec3(0, 0)):
        u"""
        Draws the ScreenMouse with its sprite. The 'hot spot' is defined by 
        the sprite offset.
        
        :TODO:
            rewrite to only render in the correct renderer
        """
        if self.hover_renderer and self.hover_renderer.rect.size == screen_surf.get_size() and self._sprites:
            spr = self._sprites[-1]
            screen_surf.blit(spr.image, (self.screen_pos - screen_offset - spr.offset).as_xy_tuple())

    #-- world registration --#
    def register_world(self, world):
        u"""
        The worlds that the mouse should interact have to be registered.
        By doing so, the ScreenMouse is added to the worls as normal entity.
        """
        world.add_entity(self)
        self._worlds.insort(world)

    def unregister_world(self, world):
        u"""
        Remove a world and stop interact with it.
        """
        if world in self._worlds:
            self._worlds.remove(world)
            world.remove_entity(self)

    #-- sprites interaction --#
    def push_sprite(self, sprite):
        u"""
        Set a different sprite. The mouse will use this immediatly.
        """
        self._sprites.append(sprite)

    def pop_sprite(self):
        u"""
        Remove a previously pushed sprite. Make sure there is always
        a sprite left (last should always be the default sprite) or you wont 
        see a mouse.
        """
        return self._sprites.pop()