"""Displays a row with filters tokens.
Part of the tagit module.
A copy of the license is provided with the project.
Author: Matthias Baumgartner, 2016
"""
# imports
from os.path import join, dirname
from kivy.lang import Builder
from kivy.uix.boxlayout import BoxLayout
from kivy.uix.label import Label
from kivy.logger import Logger
from kivy.uix.behaviors import FocusBehavior
from kivy.graphics.instructions import InstructionGroup
from kivy.graphics import Rectangle, Color
from kivy.uix.textinput import TextInput
# inner-module imports
from dialogues import TextInputDialogue, ErrorDialogue
from ..controller.filter import CFilter, CFilterToken
# exports
__all__ = ('VFilter', )
# Load kv
Builder.load_file(join(dirname(__file__), 'filter.kv'))
# classes
[docs]class VFilter(BoxLayout):
"""A row of filter tokens (i.e. some filter text) and its management.
A filter is used to search in the image database. The filter consists
of several tokens. Each token adds a search constraint.
Tokens can be added, changed and removed. The filter can be applied
to an image source. This has effects on other parts of the UI, namely
the *Browser*.
"""
def __init__(self, **kwargs):
super(VFilter, self).__init__(**kwargs)
[docs] def on_parent(self, *args):
"""Set the controller."""
self.controller = self.get_controller(CFilter)
super(VFilter, self).on_parent(*args)
[docs] def redraw(self, tokens, cutoff):
"""Redraw the tokens."""
self.tokens.redraw(tokens, cutoff)
[docs] def token_dialogue(self, text, clbk):
"""Show a dialogue for modifying tokens."""
keyctrl = self.controller.get_root().widget
dlg = TextInputDialogue(text=text, keyboard_ctrl=keyctrl)
def ok(c):
try:
clbk(c.text)
except ValueError, e:
self.error("syntax error: " + e.message)
except Exception, e:
self.error(e.message)
dlg.bind(on_ok=ok)
dlg.open()
[docs] def error(self, text):
"""Display an error dialogue."""
dlg = ErrorDialogue(text=text)
dlg.open()
[docs] def request_add_token(self):
"""Ask the controller to add a token."""
self.controller.request_add_token()
[docs] def request_apply(self):
"""Ask the controller to apply the filter."""
self.controller.request_apply()
def show_editor(self, tokens, clbk_ok, clbk_cancel):
self.tokens.editor(tokens, clbk_ok, clbk_cancel)
class TokenGroup(BoxLayout):
"""Group of tokens; i.e. the space where tokens are displayed.
"""
def redraw(self, tokens, cutoff):
"""Redraw the tokens."""
self.clear_widgets()
for idx, tok in enumerate(tokens):
wx = Token(tok)
wx.default_text = tok.display()
wx.active = idx < cutoff
self.add_widget(wx)
def editor(self, text, clbk_ok, clbk_cancel):
self.clear_widgets()
wx = TokenEditor(
text=text
, keyboard_ctrl=self.get_controller().get_root().widget
, on_ok=clbk_ok
, on_cancel=clbk_cancel
)
self.add_widget(wx)
wx.focus = True
class TokenEditor(TextInput):
"""
When invoked -> get exclusive keyboard access
when removed -> release exclusive keyboard access
On press enter -> set filter, redraw normal filter, apply
On press esc -> abort, redraw normal filter, apply
"""
def __init__(self, *args, **kwargs):
self._keyboard_ctrl = kwargs.pop('keyboard_ctrl', None)
self.clbk_accept = kwargs.pop('on_ok', None)
self.clbk_cancel= kwargs.pop('on_cancel', None)
super(TokenEditor, self).__init__(*args, **kwargs)
self._keyboard_ctrl.request_exclusive_keyboard()
self.accepted = False
def __del__(self):
self._keyboard_ctrl.release_exclusive_keyboard()
def on_text_focus(self, instance, value):
if not value and not self.accepted:
self.cancel()
def ok(self):
self.accepted = True
self._keyboard_ctrl.release_exclusive_keyboard()
self.clbk_accept(self.text)
def cancel(self):
self._keyboard_ctrl.release_exclusive_keyboard()
self.clbk_cancel(self.text)
class Token(BoxLayout):
"""A single token.
"""
def __init__(self, token, *args, **kwargs):
super(Token, self).__init__(*args, **kwargs)
self._deco = None
self.token = token
def on_parent(self, *args):
self.controller = self.get_controller(CFilterToken)
super(Token, self).on_parent(*args)
def on_size(self, *args):
self._redraw()
def on_active(self):
self._redraw()
def _redraw(self):
if self.active:
if self._deco is not None: # Remove deco
self.canvas.before.remove(self._deco)
# create deco
self._deco = InstructionGroup()
self._deco.add(Color(0, 0, 1, 0.25))
self._deco.add(Rectangle(pos=self.pos, size=self.size))
self.canvas.before.add(self._deco)
def remove(self):
"""Remove the token (Click on remove button)."""
if self.controller is not None:
self.controller.parent.request_remove_token(self.token)
def edit_label(self, text):
"""Edit the token (Click on label).
Opens a dialogue for entering text.
"""
if self.controller is None:
return
keyctrl = self.controller.get_root().widget
dlg = TextInputDialogue(text=self.token.edit(), keyboard_ctrl=keyctrl)
dlg.bind(on_ok=lambda c: self.controller.parent.request_edit_token(self.token, c.text))
dlg.open()
class TokenLabel(Label):
"""Label to display the token text.
"""
def on_touch_down(self, touch):
"""Change the token text by clicking onto it.
"""
if self.collide_point(*touch.pos):
FocusBehavior.ignored_touch.append(touch) # Stops dialogue from loosing the focus
self.parent.edit_label(self.text)
return True
return super(TokenLabel, self).on_touch_down(touch)
## EOF ##