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