Add mouse-over row highlighting
							parent
							
								
									3ea28f04a4
								
							
						
					
					
						commit
						488bdb34be
					
				| 
						 | 
					@ -0,0 +1,60 @@
 | 
				
			||||||
 | 
					"""Hoverable Behaviour (changing when the mouse is on the widget by O. Poyen.
 | 
				
			||||||
 | 
					License: LGPL
 | 
				
			||||||
 | 
					"""
 | 
				
			||||||
 | 
					__author__ = 'Olivier Poyen'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from kivy.properties import BooleanProperty, ObjectProperty
 | 
				
			||||||
 | 
					from kivy.factory import Factory
 | 
				
			||||||
 | 
					from kivy.core.window import Window
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HoverBehavior(object):
 | 
				
			||||||
 | 
					    """Hover behavior.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    :Events:
 | 
				
			||||||
 | 
					        `on_enter`
 | 
				
			||||||
 | 
					            Fired when mouse enter the bbox of the widget.
 | 
				
			||||||
 | 
					        `on_leave`
 | 
				
			||||||
 | 
					            Fired when the mouse exit the widget.
 | 
				
			||||||
 | 
					    """
 | 
				
			||||||
 | 
					    hovered = BooleanProperty(False)
 | 
				
			||||||
 | 
					    # Contains the last relevant point received by the Hoverable. This can
 | 
				
			||||||
 | 
					    # be used in `on_enter` or `on_leave` in order to know where was dispatched
 | 
				
			||||||
 | 
					    # the event.
 | 
				
			||||||
 | 
					    border_point = ObjectProperty(None)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, **kwargs):
 | 
				
			||||||
 | 
					        self.register_event_type('on_enter')
 | 
				
			||||||
 | 
					        self.register_event_type('on_leave')
 | 
				
			||||||
 | 
					        Window.bind(mouse_pos=self.on_mouse_pos)
 | 
				
			||||||
 | 
					        super(HoverBehavior, self).__init__(**kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def on_mouse_pos(self, *args):
 | 
				
			||||||
 | 
					        # do proceed if I'm not displayed <=> If have no parent
 | 
				
			||||||
 | 
					        if not self.get_root_window():
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        pos = args[1]
 | 
				
			||||||
 | 
					        # Next line to_widget allow to compensate for relative layout
 | 
				
			||||||
 | 
					        inside = self.collide_point(*self.to_widget(*pos))
 | 
				
			||||||
 | 
					        if self.hovered == inside:
 | 
				
			||||||
 | 
					            # We have already done what was needed
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        self.border_point = pos
 | 
				
			||||||
 | 
					        self.hovered = inside
 | 
				
			||||||
 | 
					        if inside:
 | 
				
			||||||
 | 
					            self.dispatch('on_enter')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            self.dispatch('on_leave')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    # implement these in the widget impl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def on_enter(self):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def on_leave(self):
 | 
				
			||||||
 | 
					        pass
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# register for global use via kivy.factory.Factory
 | 
				
			||||||
 | 
					Factory.register('HoverBehavior', HoverBehavior)
 | 
				
			||||||
| 
						 | 
					@ -19,9 +19,11 @@ from kivy.lang import Builder
 | 
				
			||||||
from kivy import utils
 | 
					from kivy import utils
 | 
				
			||||||
from kivy.app import async_runTouchApp
 | 
					from kivy.app import async_runTouchApp
 | 
				
			||||||
from kivy.core.window import Window
 | 
					from kivy.core.window import Window
 | 
				
			||||||
 | 
					from kivy.graphics import Color, Rectangle, RoundedRectangle
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from ..log import get_logger
 | 
					from ..log import get_logger
 | 
				
			||||||
from .pager import PagerView
 | 
					from .pager import PagerView
 | 
				
			||||||
 | 
					from .kivy.hoverable import HoverBehavior
 | 
				
			||||||
 | 
					
 | 
				
			||||||
log = get_logger('monitor')
 | 
					log = get_logger('monitor')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -44,13 +46,16 @@ def colorcode(name):
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_bs = 0.75  # border size
 | 
					_bs = 0.75  # border size
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# medium shade of gray that seems to match the
 | 
					# medium shade of gray that seems to match the
 | 
				
			||||||
# default i3 window borders
 | 
					# default i3 window borders
 | 
				
			||||||
_i3_rgba = [0.14]*3 + [1]
 | 
					_i3_rgba = [0.14]*3 + [1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# slightly off black like the jellybean bg from
 | 
					# slightly off black like the jellybean bg from
 | 
				
			||||||
# vim colorscheme
 | 
					# vim colorscheme
 | 
				
			||||||
_cell_rgba = [0.07]*3 + [1]
 | 
					_cell_rgba = [0.07]*3 + [1]
 | 
				
			||||||
_black_rgba = [0]*4
 | 
					_black_rgba = [0]*4
 | 
				
			||||||
 | 
					
 | 
				
			||||||
_kv = (f'''
 | 
					_kv = (f'''
 | 
				
			||||||
#:kivy 1.10.0
 | 
					#:kivy 1.10.0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
| 
						 | 
					@ -128,6 +133,50 @@ _kv = (f'''
 | 
				
			||||||
''')
 | 
					''')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class HighlightRowHoverable(HoverBehavior):
 | 
				
			||||||
 | 
					    def on_enter(self):
 | 
				
			||||||
 | 
					        """Highlight row on enter.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        log.debug(
 | 
				
			||||||
 | 
					            f"Entered cell {self} through {self.border_point}")
 | 
				
			||||||
 | 
					        row = self
 | 
				
			||||||
 | 
					        if row.mouse_over or row.is_header:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # add draw instructions
 | 
				
			||||||
 | 
					        color = row._color = Color(*_i3_rgba)
 | 
				
			||||||
 | 
					        rect = row._rect = RoundedRectangle(
 | 
				
			||||||
 | 
					            size=row.size,
 | 
				
			||||||
 | 
					            pos=row.pos,
 | 
				
			||||||
 | 
					            radius=(10,)
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        # add to canvas
 | 
				
			||||||
 | 
					        canvas = row.canvas
 | 
				
			||||||
 | 
					        if row._rect not in canvas.before.children:
 | 
				
			||||||
 | 
					            canvas.before.add(color)
 | 
				
			||||||
 | 
					            canvas.before.add(rect)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # mark row as being "selected"
 | 
				
			||||||
 | 
					            row.mouse_over = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def on_leave(self):
 | 
				
			||||||
 | 
					        """Un-highlight row on exit.
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        log.debug(
 | 
				
			||||||
 | 
					            f"Left cell {self} through {self.border_point}")
 | 
				
			||||||
 | 
					        row = self
 | 
				
			||||||
 | 
					        if not row.mouse_over or row.is_header:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					        canvas = row.canvas
 | 
				
			||||||
 | 
					        # remove instructions from canvas
 | 
				
			||||||
 | 
					        if row._color in canvas.before.children:
 | 
				
			||||||
 | 
					            canvas.before.remove(row._color)
 | 
				
			||||||
 | 
					            canvas.before.remove(row._rect)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            # mark row as being "un-selected"
 | 
				
			||||||
 | 
					            row.mouse_over = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class HeaderCell(Button):
 | 
					class HeaderCell(Button):
 | 
				
			||||||
    """Column header cell label.
 | 
					    """Column header cell label.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
| 
						 | 
					@ -224,7 +273,7 @@ class BidAskLayout(StackLayout):
 | 
				
			||||||
        return [self.last, self.bid, self.ask]
 | 
					        return [self.last, self.bid, self.ask]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Row(GridLayout):
 | 
					class Row(GridLayout, HighlightRowHoverable):
 | 
				
			||||||
    """A grid for displaying a row of ticker quote data.
 | 
					    """A grid for displaying a row of ticker quote data.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    The row fields can be updated using the ``fields`` property which will in
 | 
					    The row fields can be updated using the ``fields`` property which will in
 | 
				
			||||||
| 
						 | 
					@ -232,14 +281,17 @@ class Row(GridLayout):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    def __init__(
 | 
					    def __init__(
 | 
				
			||||||
        self, record, headers=(), bidasks=None, table=None,
 | 
					        self, record, headers=(), bidasks=None, table=None,
 | 
				
			||||||
        is_header_row=False,
 | 
					        is_header=False,
 | 
				
			||||||
        **kwargs
 | 
					        **kwargs
 | 
				
			||||||
    ):
 | 
					    ):
 | 
				
			||||||
        super(Row, self).__init__(cols=len(record), **kwargs)
 | 
					        super(Row, self).__init__(cols=len(record), **kwargs)
 | 
				
			||||||
        self._cell_widgets = {}
 | 
					        self._cell_widgets = {}
 | 
				
			||||||
        self._last_record = record
 | 
					        self._last_record = record
 | 
				
			||||||
        self.table = table
 | 
					        self.table = table
 | 
				
			||||||
        self.is_header = is_header_row
 | 
					        self.is_header = is_header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # selection state
 | 
				
			||||||
 | 
					        self.mouse_over = False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # create `BidAskCells` first
 | 
					        # create `BidAskCells` first
 | 
				
			||||||
        layouts = {}
 | 
					        layouts = {}
 | 
				
			||||||
| 
						 | 
					@ -248,7 +300,7 @@ class Row(GridLayout):
 | 
				
			||||||
        for key, children in bidasks.items():
 | 
					        for key, children in bidasks.items():
 | 
				
			||||||
            layout = BidAskLayout(
 | 
					            layout = BidAskLayout(
 | 
				
			||||||
                [record[key]] + [record[child] for child in children],
 | 
					                [record[key]] + [record[child] for child in children],
 | 
				
			||||||
                header=is_header_row
 | 
					                header=is_header
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
            layout.row = self
 | 
					            layout.row = self
 | 
				
			||||||
            layouts[key] = layout
 | 
					            layouts[key] = layout
 | 
				
			||||||
| 
						 | 
					@ -518,7 +570,7 @@ async def _async_main(
 | 
				
			||||||
            {key: key for key in headers},
 | 
					            {key: key for key in headers},
 | 
				
			||||||
            headers=headers,
 | 
					            headers=headers,
 | 
				
			||||||
            bidasks=bidasks,
 | 
					            bidasks=bidasks,
 | 
				
			||||||
            is_header_row=True,
 | 
					            is_header=True,
 | 
				
			||||||
            size_hint=(1, None),
 | 
					            size_hint=(1, None),
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
        box.add_widget(header)
 | 
					        box.add_widget(header)
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue