"""
This module contains code for ClickableText
"""
import urwid
from urwid.util import is_mouse_press
import lookatme.config as config
from lookatme.utils import spec_from_style, row_text
[docs]class LinkIndicatorSpec(urwid.AttrSpec):
"""Used to track a link within an urwid.Text instance
"""
def __init__(self, link_label, link_target, orig_spec):
"""Create a new LinkIndicator spec from an existing urwid.AttrSpec
:param str link_label: The label for the link
:param str link_target: The target url for the link
"""
self.link_label = link_label
self.link_target = link_target
urwid.AttrSpec.__init__(self, orig_spec.foreground, orig_spec.background)
[docs]class ClickableText(urwid.Text):
"""Allows clickable/changing text to be part of the Text() contents
"""
signals = ["click", "change"]
[docs] def mouse_event(self, size, event, button, x, y, focus):
"""Handle mouse events!
"""
if button != 1 or not is_mouse_press(event):
return False
rendered = self.render(size).content()
total_text = b"\n".join(row_text(x) for x in rendered)
total_offset = (y * size[0]) + x
text, chunk_stylings = self.get_text()
curr_offset = 0
found_style = None
found_text = None
found_idx = 0
found_length = 0
for idx, info in enumerate(chunk_stylings):
style, length = info
if curr_offset < total_offset <= curr_offset + length:
found_text = text[curr_offset:curr_offset + length]
found_style = style
found_idx = idx
found_length = length
break
curr_offset += length
if found_style is None or not isinstance(found_style, LinkIndicatorSpec):
self._emit('click')
return True
# it's a link, so change the text and update the RLE!
if found_text == found_style.link_label:
new_text = found_style.link_target
else:
new_text = found_style.link_label
text = text[:curr_offset] + new_text + text[curr_offset+found_length:]
new_rle = len(new_text)
chunk_stylings[found_idx] = (found_style, new_rle)
self._text = text
self._attrib = chunk_stylings
self._invalidate()
self._emit("change")
return True