Add mouse-over row highlighting

kivy_mainline_and_py3.8
Tyler Goodlet 2018-11-23 22:21:53 -05:00
parent 3ea28f04a4
commit 488bdb34be
3 changed files with 117 additions and 5 deletions

View File

View File

@ -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)

View File

@ -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)