'''Label
=====

.. image:: images/label.png
    :align: right

The :class:`Label` widget is for rendering text. It supports ascii and unicode
strings::

    # hello world text
    l = Label(text='Hello world')

    # unicode text; can only display glyphs that are available in the font
    l = Label(text=u'Hello world ' + unichr(2764))

    # multiline text
    l = Label(text='Multi\\nLine')

    # size
    l = Label(text='Hello world', font_size='20sp')

.. _kivy-uix-label-sizing-and-text-content:

Sizing and text content
---------------------------

By default, the size of :class:`Label` is not affected by :attr:`~Label.text`
content and the text is not affected by the size. In order to control
sizing, you must specify :attr:`~Label.text_size` to constrain the text
and/or bind :attr:`~Label.size` to :attr:`~Label.texture_size` to grow with
the text.

For example, this label's size will be set to the text content
(plus :attr:`~Label.padding`):

.. code-block:: kv

    Label:
        size: self.texture_size

This label's text will wrap at the specified width and be clipped to the
height:

.. code-block:: kv

    Label:
        text_size: cm(6), cm(4)

.. note:: The :attr:`~Label.shorten` and :attr:`~Label.max_lines` attributes
 control how overflowing text behaves.

Combine these concepts to create a Label that can grow vertically but wraps the
text at a certain width:

.. code-block:: kv

    Label:
        text_size: root.width, None
        size: self.texture_size

Text alignment and wrapping
---------------------------

The :class:`Label` has :attr:`~Label.halign` and :attr:`~Label.valign`
properties to control the alignment of its text. However, by default the text
image (:attr:`~Label.texture`) is only just large enough to contain the
characters and is positioned in the center of the Label. The valign property
will have no effect and halign will only have an effect if your text has
newlines; a single line of text will appear to be centered even though halign
is set to left (by default).

In order for the alignment properties to take effect, set the
:attr:`~Label.text_size`, which specifies the size of the bounding box within
which text is aligned. For instance, the following code binds this size to the
size of the Label, so text will be aligned within the widget bounds. This
will also automatically wrap the text of the Label to remain within this area.

.. code-block:: kv

    Label:
        text_size: self.size
        halign: 'right'
        valign: 'middle'

Markup text
-----------

.. versionadded:: 1.1.0

You can change the style of the text using :doc:`api-kivy.core.text.markup`.
The syntax is similar to the bbcode syntax but only the inline styling is
allowed::

    # hello world with world in bold
    l = Label(text='Hello [b]World[/b]', markup=True)

    # hello in red, world in blue
    l = Label(text='[color=ff3333]Hello[/color][color=3333ff]World[/color]',
        markup = True)

If you need to escape the markup from the current text, use
:func:`kivy.utils.escape_markup`::

    text = 'This is an important message [1]'
    l = Label(text='[b]' + escape_markup(text) + '[/b]', markup=True)

The following tags are available:

``[b][/b]``
    Activate bold text
``[i][/i]``
    Activate italic text
``[u][/u]``
    Underlined text
``[s][/s]``
    Strikethrough text
``[font=<str>][/font]``
    Change the font
``[size=<integer>][/size]``
    Change the font size
``[color=#<color>][/color]``
    Change the text color
``[ref=<str>][/ref]``
    Add an interactive zone. The reference + bounding box inside the
    reference will be available in :attr:`Label.refs`
``[anchor=<str>]``
    Put an anchor in the text. You can get the position of your anchor within
    the text with :attr:`Label.anchors`
``[sub][/sub]``
    Display the text at a subscript position relative to the text before it.
``[sup][/sup]``
    Display the text at a superscript position relative to the text before it.

If you want to render the markup text with a [ or ] or & character, you need to
escape them. We created a simple syntax::

    [   -> &bl;
    ]   -> &br;
    &   -> &amp;

Then you can write::

    "[size=24]Hello &bl;World&br;[/size]"

Interactive zone in text
------------------------

.. versionadded:: 1.1.0

You can now have definable "links" using text markup. The idea is to be able
to detect when the user clicks on part of the text and to react.
The tag ``[ref=xxx]`` is used for that.

In this example, we are creating a reference on the word "World". When
this word is clicked, the function ``print_it`` will be called with the
name of the reference::

    def print_it(instance, value):
        print('User clicked on', value)
    widget = Label(text='Hello [ref=world]World[/ref]', markup=True)
    widget.bind(on_ref_press=print_it)

For prettier rendering, you could add a color for the reference. Replace the
``text=`` in the previous example with::

    'Hello [ref=world][color=0000ff]World[/color][/ref]'

Catering for Unicode languages
------------------------------

The font kivy uses does not contain all the characters required for displaying
all languages. When you use the built-in widgets, this results in a block being
drawn where you expect a character.

If you want to display such characters, you can chose a font that supports them
and deploy it universally via kv:

.. code-block:: kv

    <Label>:
        font_name: '/<path>/<to>/<font>'

Note that this needs to be done before your widgets are loaded as kv rules are
only applied at load time.

Usage example
-------------

The following example marks the anchors and references contained in a label::

    from kivy.app import App
    from kivy.uix.label import Label
    from kivy.clock import Clock
    from kivy.graphics import Color, Rectangle


    class TestApp(App):

        @staticmethod
        def get_x(label, ref_x):
            """ Return the x value of the ref/anchor relative to the canvas """
            return label.center_x - label.texture_size[0] * 0.5 + ref_x

        @staticmethod
        def get_y(label, ref_y):
            """ Return the y value of the ref/anchor relative to the canvas """
            # Note the inversion of direction, as y values start at the top of
            # the texture and increase downwards
            return label.center_y + label.texture_size[1] * 0.5 - ref_y

        def show_marks(self, label):

            # Indicate the position of the anchors with a red top marker
            for name, anc in label.anchors.items():
                with label.canvas:
                    Color(1, 0, 0)
                    Rectangle(pos=(self.get_x(label, anc[0]),
                                   self.get_y(label, anc[1])),
                              size=(3, 3))

            # Draw a green surround around the refs. Note the sizes y inversion
            for name, boxes in label.refs.items():
                for box in boxes:
                    with label.canvas:
                        Color(0, 1, 0, 0.25)
                        Rectangle(pos=(self.get_x(label, box[0]),
                                       self.get_y(label, box[1])),
                                  size=(box[2] - box[0],
                                        box[1] - box[3]))

        def build(self):
            label = Label(
                text='[anchor=a]a\\nChars [anchor=b]b\\n[ref=myref]ref[/ref]',
                markup=True)
            Clock.schedule_once(lambda dt: self.show_marks(label), 1)
            return label

    TestApp().run()

'''

__all__ = ('Label', )

from kivy.clock import Clock
from kivy.uix.widget import Widget
from kivy.core.text import Label as CoreLabel, DEFAULT_FONT
from kivy.core.text.markup import MarkupLabel as CoreMarkupLabel
from kivy.properties import StringProperty, OptionProperty, \
    NumericProperty, BooleanProperty, ReferenceListProperty, \
    ListProperty, ObjectProperty, DictProperty
from kivy.utils import get_hex_from_color


class Label(Widget):
    '''Label class, see module documentation for more information.

    :Events:
        `on_ref_press`
            Fired when the user clicks on a word referenced with a
            ``[ref]`` tag in a text markup.
    '''

    __events__ = ['on_ref_press']

    _font_properties = ('text', 'font_size', 'font_name', 'bold', 'italic',
                        'underline', 'strikethrough', 'color',
                        'disabled_color', 'halign', 'valign', 'padding_x',
                        'padding_y', 'outline_width', 'disabled_outline_color',
                        'outline_color', 'text_size', 'shorten', 'mipmap',
                        'line_height', 'max_lines', 'strip', 'shorten_from',
                        'split_str', 'ellipsis_options', 'unicode_errors',
                        'markup', 'font_hinting', 'font_kerning',
                        'font_blended')

    def __init__(self, **kwargs):
        self._trigger_texture = Clock.create_trigger(self.texture_update, -1)
        super(Label, self).__init__(**kwargs)

        # bind all the property for recreating the texture
        d = Label._font_properties
        fbind = self.fbind
        update = self._trigger_texture_update
        fbind('disabled', update, 'disabled')
        for x in d:
            fbind(x, update, x)

        self._label = None
        self._create_label()

        # force the texture creation
        self._trigger_texture()

    def _create_label(self):
        # create the core label class according to markup value
        if self._label is not None:
            cls = self._label.__class__
        else:
            cls = None
        markup = self.markup
        if (markup and cls is not CoreMarkupLabel) or \
           (not markup and cls is not CoreLabel):
            # markup have change, we need to change our rendering method.
            d = Label._font_properties
            dkw = dict(list(zip(d, [getattr(self, x) for x in d])))
            if markup:
                self._label = CoreMarkupLabel(**dkw)
            else:
                self._label = CoreLabel(**dkw)

    def _trigger_texture_update(self, name=None, source=None, value=None):
        # check if the label core class need to be switch to a new one
        if name == 'markup':