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