<commit_before># -*- coding: utf-8 -*-
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
from builtins import chr
import unittest
from mock.mock import MagicMock
from asciimatics.event import KeyboardEvent, MouseEvent
from asciimatics.exceptions import NextScene, StopApplication, InvalidFields
from asciimatics.scene import Scene
from asciimatics.screen import Screen, Canvas
from asciimatics.widgets import Frame, Layout, Button, Label, TextBox, Text, \
    Divider, RadioButtons, CheckBox, PopUpDialog, ListBox, Widget, MultiColumnListBox


class TestFrame(Frame):
    def __init__(self, screen, has_border=True, reduce_cpu=False):
        super(TestFrame, self).__init__(screen,
                                        screen.height,
                                        screen.width,
                                        name="Test Form",
                                        has_border=has_border,
                                        hover_focus=True,
                                        reduce_cpu=reduce_cpu)
        layout = Layout([1, 18, 1])
        self.add_layout(layout)
        self._reset_button = Button("Reset", self._reset)
        layout.add_widget(Label("Group 1:"), 1)
        layout.add_widget(TextBox(5,
                                  label="My First Box:",
                                  name="TA",
                                  on_change=self._on_change), 1)
        layout.add_widget(
            Text(label="Text1:", name="TB", on_change=self._on_change), 1)
        layout.add_widget(
            Text(label="Text2:",
                 name="TC",
                 on_change=self._on_change,
                 validator="^[0-9]*$"), 1)
        layout.add_widget(
            Text(label="Text3:",
                 name="TD",
                 on_change=self._on_change,
                 validator=lambda x: x in ("", "a")), 1)
        layout.add_widget(Divider(height=2), 1)
        layout.add_widget(Label("Group 2:"), 1)
        layout.add_widget(RadioButtons([("Option 1", 1),
                                        ("Option 2", 2),
                                        ("Option 3", 3)],
                                       label="A Longer Selection:",
                                       name="Things",
                                       on_change=self._on_change), 1)
        layout.add_widget(CheckBox("Field 1",
                                   label="A very silly long name for fields:",
                                   name="CA",
                                   on_change=self._on_change), 1)
        layout.add_widget(
            CheckBox("Field 2", name="CB", on_change=self._on_change), 1)
        layout.add_widget(
            CheckBox("Field 3", name="CC", on_change=self._on_change), 1)
        layout.add_widget(Divider(height=3), 1)
        layout2 = Layout([1, 1, 1])
        self.add_layout(layout2)
        layout2.add_widget(self._reset_button, 0)
        layout2.add_widget(Button("View Data", self._view), 1)
        layout2.add_widget(Button("Quit", self._quit), 2)
        self.fix()

    def _on_change(self):
        changed = False
        self.save()
        for key, value in self.data.items():
            if isinstance(value, bool):
                if value:
                    changed = True
                    break
            if isinstance(value, int):
                if value != 1:
                    changed = True
                    break
            elif value is None:
                continue
            elif len(value) != 0:
                changed = True
                break
        self._reset_button.disabled = not changed

    def _reset(self):
        self.reset()
        raise NextScene()

    def _view(self):
        # Build result of this form and display it.
        self.save()
        raise NextScene()

    @staticmethod
    def _quit():
        raise StopApplication("User requested exit")


class TestFrame2(Frame):
    def __init__(self, screen, init_values):
        super(TestFrame2, self).__init__(screen,
                                         screen.height,
                                         screen.width,
                                         title="Test Frame 2")
        # Create the form for displaying the list of contacts.
        self._list_view = ListBox(
            Widget.FILL_FRAME,
            init_values,
            name="contacts",
            on_change=self._on_pick)
        self._edit_button = Button("Edit", self._edit)
        self._delete_button = Button("Delete", self._delete)
        layout = Layout([100], fill_frame=True)
        self.add_layout(layout)
        layout.add_widget(self._list_view)
        layout.add_widget(Divider())
        layout2 = Layout([1, 1, 1, 1])
        self.add_layout(layout2)
        layout2.add_widget(Button("Add", self._add), 0)
        layout2.add_widget(self._edit_button, 1)
        layout2.add_widget(self._delete_button, 2)
        layout2.add_widget(Button("Quit", self._quit), 3)
        self.fix()
        self._on_pick()

    def _on_pick(self):
        self._edit_button.disabled = self._list_view.value is None
        self._delete_button.disabled = self._list_view.value is None

    @staticmethod
    def _add():
        raise NextScene("Add")

    @staticmethod
    def _edit():
        raise NextScene("Edit")

    @staticmethod
    def _delete():
        raise NextScene("Delete")

    @staticmethod
    def _quit():
        raise StopApplication("User pressed quit")


class TestFrame3(Frame):
    def __init__(self, screen):
        super(TestFrame3, self).__init__(screen, 10, 20,
                                         name="Blank",
                                         has_shadow=True)
        self.fix()


class TestWidgets(unittest.TestCase):

    def assert_canvas_equals(self, canvas, expected):
        """
        Assert output to canvas is as expected.
        """
        output = ""
        for y in range(canvas.height):
            for x in range(canvas.width):
                char, _, _, _ = canvas.get_from(x, y)
                output += chr(char)
            output += "\n"
        self.assertEqual(output, expected)

    @staticmethod
    def process_keys(form, values, separator=None):
        """
        Inject a set of key events separated by a common key separator.
        """
        for new_value in values:
            if isinstance(new_value, int):
                form.process_event(KeyboardEvent(new_value))
            else:
                for char in new_value:
                    form.process_event(KeyboardEvent(ord(char)))
            if separator:
                form.process_event(KeyboardEvent(separator))

    @staticmethod
    def process_mouse(form, values):
        """
        Inject a set of mouse events.
        """
        for x, y, buttons in values:
            form.process_event(MouseEvent(x, y, buttons))

    def test_form_data(self):
        """
        Check Frame.data works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)

        # Should be empty on construction
        self.assertEqual(form.data, {})

        # Should be blank values after saving.
        form.reset()
        form.save()
        self.assertEqual(
            form.data,
            {
                'CA': False,
                'CB': False,
                'CC': False,
                'TA': [''],
                'TB': '',
                'TC': '',
                'TD': '',
                'Things': 1
            })

    def test_rendering(self):
        """
        Check Frame renders as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check initial rendering
        form.update(0)
        self.assert_canvas_equals(
            canvas,
            "+--------------------------------------+\n" +
            "| Group 1:                             |\n" +
            "| My First                             O\n" +
            "| Box:                                 |\n" +
            "|                                      |\n" +
            "|                                      |\n" +
            "|                                      |\n" +
            "| Text1:                               |\n" +
            "| Text2:                               |\n" +
            "+--------------------------------------+\n")

        # Check scrolling works.  Should also test label splitting and ellipsis.
        form.move_to(0, 9, 8)
        form.update(1)
        self.assert_canvas_equals(
            canvas,
            "+--------------------------------------+\n" +
            "| Text3:                               |\n" +
            "|                                      |\n" +
            "| ----------------------------------   |\n" +
            "| Group 2:                             O\n" +
            "| A Longer   (X) Option 1              |\n" +
            "| Selection: ( ) Option 2              |\n" +
            "|            ( ) Option 3              |\n" +
            "| A very...  [ ] Field 1               |\n" +
            "+--------------------------------------+\n")

        # Now check button rendering.
        form.move_to(0, 18, 8)
        form.update(2)
        self.assert_canvas_equals(
            canvas,
            "+--------------------------------------+\n" +
            "|            [ ] Field 3               |\n" +
            "|                                      |\n" +
            "| ----------------------------------   |\n" +
            "|                                      |\n" +
            "| < Reset >  < View Data > < Quit >    |\n" +
            "|                                      |\n" +
            "|                                      O\n" +
            "|                                      |\n" +
            "+--------------------------------------+\n")

    def test_unicode_rendering(self):
        """
        Check Frame renders as expected for unicode environments.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=True)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check initial rendering
        form.update(0)
        self.assert_canvas_equals(
            canvas,
            "┌──────────────────────────────────────┐\n" +
            "│ Group 1:                             │\n" +
            "│ My First                             █\n" +
            "│ Box:                                 ░\n" +
            "│                                      ░\n" +
            "│                                      ░\n" +
            "│                                      ░\n" +
            "│ Text1:                               ░\n" +
            "│ Text2:                               │\n" +
            "└──────────────────────────────────────┘\n")

        # Check scrolling works.  Should also test label splitting and ellipsis.
        form.move_to(0, 9, 8)
        form.update(1)
        self.assert_canvas_equals(
            canvas,
            "┌──────────────────────────────────────┐\n" +
            "│ Text3:                               │\n" +
            "│                                      ░\n" +
            "│ ──────────────────────────────────   ░\n" +
            "│ Group 2:                             █\n" +
            "│ A Longer   (•) Option 1              ░\n" +
            "│ Selection: ( ) Option 2              ░\n" +
            "│            ( ) Option 3              ░\n" +
            "│ A very...  [ ] Field 1               │\n" +
            "└──────────────────────────────────────┘\n")

        # Now check button rendering.
        form.move_to(0, 18, 8)
        form.update(2)
        self.assert_canvas_equals(
            canvas,
            "┌──────────────────────────────────────┐\n" +
            "│            [ ] Field 3               │\n" +
            "│                                      ░\n" +
            "│ ──────────────────────────────────   ░\n" +
            "│                                      ░\n" +
            "│ < Reset >  < View Data > < Quit >    ░\n" +
            "│                                      ░\n" +
            "│                                      █\n" +
            "│                                      │\n" +
            "└──────────────────────────────────────┘\n")

    def test_no_border(self):
        """
        Check that a Frame with nor border works
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas, has_border=False)
        form.reset()

        # Check initial rendering
        form.update(0)
        self.assert_canvas_equals(
            canvas,
            "  Group 1:                              \n" +
            "  My First                              \n" +
            "  Box:                                  \n" +
            "                                        \n" +
            "                                        \n" +
            "                                        \n" +
            "  Text1:                                \n" +
            "  Text2:                                \n" +
            "  Text3:                                \n" +
            "                                        \n")

        # Check scrolling works.  Should also test label splitting and ellipsis.
        form.move_to(0, 10, 10)
        form.update(1)
        self.assert_canvas_equals(
            canvas,
            "  ------------------------------------  \n" +
            "  Group 2:                              \n" +
            "  A Longer    (X) Option 1              \n" +
            "  Selection:  ( ) Option 2              \n" +
            "              ( ) Option 3              \n" +
            "  A very si...[ ] Field 1               \n" +
            "              [ ] Field 2               \n" +
            "              [ ] Field 3               \n" +
            "                                        \n" +
            "  ------------------------------------  \n")

    def test_form_input(self):
        """
        Check Frame input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()
        self.process_keys(form,
                          ["ABC\nDEF", "GHI", "jkl", "MN", " ", " ", "", " "],
                          Screen.KEY_TAB)
        form.save()
        self.assertEqual(
            form.data,
            {
                'CA': True,
                'CB': False,
                'CC': True,
                'TA': ['ABC', 'DEF'],
                'TB': 'GHI',
                'TC': 'jkl',
                'TD': 'MN',
                'Things': 1
            })

        # Check that forms ignore unrecognised events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_textbox_input(self):
        """
        Check TextBox input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check basic movement keys
        self.process_keys(form,  ["ABC", Screen.KEY_LEFT, "D"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDC"])
        self.process_keys(form,  [Screen.KEY_RIGHT, Screen.KEY_RIGHT, "E"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDCE"])
        self.process_keys(form,  ["\nFGH", Screen.KEY_UP, Screen.KEY_UP, "I"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "FGH"])
        self.process_keys(form,  [Screen.KEY_DOWN, Screen.KEY_DOWN, "J"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "FGHJ"])
        self.process_keys(form,  [Screen.KEY_HOME, "K"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJ"])
        self.process_keys(form,  [Screen.KEY_END, "L"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJL"])

        # Backspace - normal and wrapping lines
        self.process_keys(form,  [Screen.KEY_BACK])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJ"])
        self.process_keys(form,  [Screen.KEY_HOME, Screen.KEY_BACK])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEKFGHJ"])

        # Check cursor line-wrapping
        self.process_keys(form,  ["\n"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJ"])
        self.process_keys(form,  [Screen.KEY_LEFT, "M"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEM", "KFGHJ"])
        self.process_keys(form,  [Screen.KEY_RIGHT, "N"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEM", "NKFGHJ"])

        # Delete - normal and wrapping lines and at end of all data.
        self.process_keys(form,  [Screen.KEY_DELETE])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEM", "NFGHJ"])
        self.process_keys(form,
                          [Screen.KEY_UP, Screen.KEY_END, Screen.KEY_DELETE])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEMNFGHJ"])
        self.process_keys(form, [Screen.KEY_END, Screen.KEY_DELETE])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEMNFGHJ"])

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_text_input(self):
        """
        Check Text input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check basic movement keys
        self.process_keys(form,  [Screen.KEY_TAB, "ABC"])
        form.save()
        self.assertEqual(form.data["TB"], "ABC")
        self.process_keys(form,  [Screen.KEY_HOME, "D"])
        form.save()
        self.assertEqual(form.data["TB"], "DABC")
        self.process_keys(form,  [Screen.KEY_END, "E"])
        form.save()
        self.assertEqual(form.data["TB"], "DABCE")
        self.process_keys(form,  [Screen.KEY_LEFT, "F"])
        form.save()
        self.assertEqual(form.data["TB"], "DABCFE")
        self.process_keys(form,  [Screen.KEY_RIGHT, "G"])
        form.save()
        self.assertEqual(form.data["TB"], "DABCFEG")

        # Backspace
        self.process_keys(form,  [Screen.KEY_BACK])
        form.save()
        self.assertEqual(form.data["TB"], "DABCFE")

        # Delete - including at end of data
        self.process_keys(form,  [Screen.KEY_DELETE])
        form.save()
        self.assertEqual(form.data["TB"], "DABCFE")
        self.process_keys(form,  [Screen.KEY_HOME, Screen.KEY_DELETE])
        form.save()
        self.assertEqual(form.data["TB"], "ABCFE")

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_validation(self):
        """
        Check free-form text validation works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check that save still works with no validation.
        self.process_keys(form,  [Screen.KEY_TAB, Screen.KEY_TAB, "ABC"])
        form.save()
        self.assertEqual(form.data["TC"], "ABC")

        # Check that enforced validation throws exceptions as needed.
        with self.assertRaises(InvalidFields) as cm:
            form.save(validate=True)
        self.assertEqual(cm.exception.fields, ["TC"])

        # Check valid data doesn't throw anything.
        self.process_keys(form,
                          [Screen.KEY_BACK, Screen.KEY_BACK, Screen.KEY_BACK])
        form.save(validate=True)

        # Check functions work as well as regexp strings.
        self.process_keys(form, [Screen.KEY_TAB, "ABC"])
        with self.assertRaises(InvalidFields) as cm:
            form.save(validate=True)
        self.assertEqual(cm.exception.fields, ["TD"])

    def test_checkbox_input(self):
        """
        Check Checkbox input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check basic selection keys
        self.process_keys(
            form,
            [Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB,
             Screen.KEY_TAB, " "])
        form.save()
        self.assertEqual(form.data["CA"], True)
        self.process_keys(form, ["\n"])
        form.save()
        self.assertEqual(form.data["CA"], False)
        self.process_keys(form, ["\r"])
        form.save()
        self.assertEqual(form.data["CA"], True)

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_radiobutton_input(self):
        """
        Check RadioButton input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check basic selection keys - including limit checking
        self.process_keys(
            form,
            [Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB])
        form.save()
        self.assertEqual(form.data["Things"], 1)
        self.process_keys(form, [Screen.KEY_UP])
        form.save()
        self.assertEqual(form.data["Things"], 1)
        self.process_keys(form, [Screen.KEY_DOWN])
        form.save()
        self.assertEqual(form.data["Things"], 2)
        self.process_keys(form, [Screen.KEY_DOWN])
        form.save()
        self.assertEqual(form.data["Things"], 3)
        self.process_keys(form, [Screen.KEY_DOWN])
        form.save()
        self.assertEqual(form.data["Things"], 3)
        self.process_keys(form, [Screen.KEY_UP])
        form.save()
        self.assertEqual(form.data["Things"], 2)

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_mouse_input(self):
        """
        Check mouse input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        scene = MagicMock(spec=Scene)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.register_scene(scene)
        form.reset()

        # Check focus moves when clicked on a text or textbox
        self.process_mouse(form, [(29, 7, MouseEvent.LEFT_CLICK)])
        self.assertEqual(form._layouts[form._focus]._live_widget, 2)
        self.process_mouse(form, [(29, 2, MouseEvent.LEFT_CLICK)])
        self.assertEqual(form._layouts[form._focus]._live_widget, 1)

        # Check focus doesn't change on scroll or click outside of widgets
        self.process_mouse(form, [(0, 0, 0)])
        self.assertEqual(form._focus, 0)
        self.assertEqual(form._layouts[form._focus]._live_col, 1)
        self.assertEqual(form._layouts[form._focus]._live_widget, 1)
        self.process_mouse(form, [(39, 7, MouseEvent.LEFT_CLICK)])
        self.assertEqual(form._layouts[form._focus]._live_widget, 1)

        # Check focus moves when clicked on a checkbox or radiobutton
        self.process_mouse(form, [(29, 1, MouseEvent.LEFT_CLICK)])
        self.assertEqual(form._layouts[form._focus]._live_widget, 7)
        # Note that the above changes the Frame start-line.
        self.process_mouse(form, [(29, 5, MouseEvent.LEFT_CLICK)])
        self.assertEqual(form._layouts[form._focus]._live_widget, 9)

        # Check focus moves when hovering over a widget
        self.process_mouse(form, [(39, 7, MouseEvent.LEFT_CLICK), (3, 8, 0)])
        self.assertEqual(form._focus, 1)
        self.assertEqual(form._layouts[form._focus]._live_col, 0)
        self.assertEqual(form._layouts[form._focus]._live_widget, 0)

        # Check button click triggers an event.
        with self.assertRaises(StopApplication):
            self.process_mouse(form, [(30, 8, MouseEvent.LEFT_CLICK)])

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_widget_navigation(self):
        """
        Check widget tab stops work as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check default focus at start is first visible widget
        self.assertEqual(form._focus, 0)
        self.assertEqual(form._layouts[form._focus]._live_col, 1)
        self.assertEqual(form._layouts[form._focus]._live_widget, 1)

        # Check BACK_TAB reverses TAB.
        self.process_keys(form, [Screen.KEY_TAB])
        self.assertEqual(form._layouts[form._focus]._live_widget, 2)
        self.process_keys(form, [Screen.KEY_BACK_TAB])
        self.assertEqual(form._layouts[form._focus]._live_widget, 1)

        # Check BACK_TAB/TAB wraps around the form.
        self.process_keys(form, [Screen.KEY_BACK_TAB])
        self.assertEqual(form._focus, 1)
        self.process_keys(form, [Screen.KEY_TAB])
        self.assertEqual(form._focus, 0)

        # Tab out into text fields and check UP/DOWN keys.
        self.process_keys(form, [Screen.KEY_TAB, Screen.KEY_DOWN])
        self.assertEqual(form._layouts[form._focus]._live_widget, 3)
        self.process_keys(form, [Screen.KEY_UP])
        self.assertEqual(form._layouts[form._focus]._live_widget, 2)

        # Tab out into buttons and check LEFT/RIGHT keys.
        self.process_keys(form, [Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB,
                                 Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB,
                                 Screen.KEY_TAB])
        self.assertEqual(form._focus, 1)
        self.assertEqual(form._layouts[form._focus]._live_col, 1)
        self.assertEqual(form._layouts[form._focus]._live_widget, 0)

        self.process_keys(form, [Screen.KEY_RIGHT])
        self.assertEqual(form._layouts[form._focus]._live_col, 2)
        self.process_keys(form, [Screen.KEY_LEFT])
        self.assertEqual(form._layouts[form._focus]._live_col, 1)

    def test_list_box(self):
        """
        Check ListBox widget works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        scene = MagicMock(spec=Scene)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame2(canvas, [("One", 1), ("Two", 2)])
        form.register_scene(scene)
        form.reset()

        # Check we have a default value for our list.
        form.save()
        self.assertEqual(form.data, {"contacts": 1})

        # Check that UP/DOWN change selection.
        self.process_keys(form, [Screen.KEY_DOWN])
        form.save()
        self.assertEqual(form.data, {"contacts": 2})
        self.process_keys(form, [Screen.KEY_UP])
        form.save()
        self.assertEqual(form.data, {"contacts": 1})

        # Check that the listbox is rendered correctly.
        form.update(0)
        self.assert_canvas_equals(
            canvas,
            "+------------ Test Frame 2 ------------+\n" +
            "|One                                   |\n" +
            "|Two                                   O\n" +
            "|                                      |\n" +
            "|                                      |\n" +
            "|                                      |\n" +
            "|                                      |\n" +
            "|--------------------------------------|\n" +
            "| < Add > < Edit > < Delete < Quit >   |\n" +
            "+--------------------------------------+\n")

        # Check that mouse input changes selection.
        self.process_mouse(form, [(2, 2, MouseEvent.LEFT_CLICK)])
        form.save()
        self.assertEqual(form.data, {"contacts": 2})
        self.process_mouse(form, [(2, 1, MouseEvent.LEFT_CLICK)])
        form.save()
        self.assertEqual(form.data, {"contacts": 1})

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_multi_column_list_box(self):
        """
        Check MultiColumnListBox works as expected.
        """
        # Create a dummy screen.
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        scene = MagicMock(spec=Scene)
        canvas = Canvas(screen, 10, 40, 0, 0)

        # Create the form we want to test.
        form = Frame(canvas, canvas.height, canvas.width, has_border=False)
        layout = Layout([100], fill_frame=True)
        mc_list = MultiColumnListBox(
            Widget.FILL_FRAME,
            [3, "4", ">4", "<4", ">10%", "100%"],
            [
                (["1", "2", "3", "4", "5", "6"], 1),
                (["11", "222", "333", "444", "555", "6"], 2),
                (["111", "2", "3", "4", "5", "6"], 3),
                (["1", "2", "33333", "4", "5", "6"], 4),
                (["1", "2", "3", "4", "5", "6666666666666666666666"], 5),
            ],
            titles=["A", "B", "C", "D", "E", "F"],
            name="mc_list")
        form.add_layout(layout)
        layout.add_widget(mc_list)<commit_msg>Added a simple testcase for blur & focus callbacks<commit_after># -*- coding: utf-8 -*-
from __future__ import division
from __future__ import absolute_import
from __future__ import print_function
from __future__ import unicode_literals
from builtins import chr
import unittest
from mock.mock import MagicMock
from asciimatics.event import KeyboardEvent, MouseEvent
from asciimatics.exceptions import NextScene, StopApplication, InvalidFields
from asciimatics.scene import Scene
from asciimatics.screen import Screen, Canvas
from asciimatics.widgets import Frame, Layout, Button, Label, TextBox, Text, \
    Divider, RadioButtons, CheckBox, PopUpDialog, ListBox, Widget, MultiColumnListBox


class TestFrame(Frame):
    def __init__(self, screen, has_border=True, reduce_cpu=False):
        super(TestFrame, self).__init__(screen,
                                        screen.height,
                                        screen.width,
                                        name="Test Form",
                                        has_border=has_border,
                                        hover_focus=True,
                                        reduce_cpu=reduce_cpu)
        layout = Layout([1, 18, 1])
        self.add_layout(layout)
        self._reset_button = Button("Reset", self._reset)
        layout.add_widget(Label("Group 1:"), 1)
        layout.add_widget(TextBox(5,
                                  label="My First Box:",
                                  name="TA",
                                  on_change=self._on_change), 1)
        layout.add_widget(
            Text(label="Text1:", name="TB", on_change=self._on_change), 1)
        layout.add_widget(
            Text(label="Text2:",
                 name="TC",
                 on_change=self._on_change,
                 validator="^[0-9]*$"), 1)
        layout.add_widget(
            Text(label="Text3:",
                 name="TD",
                 on_change=self._on_change,
                 validator=lambda x: x in ("", "a")), 1)
        layout.add_widget(Divider(height=2), 1)
        layout.add_widget(Label("Group 2:"), 1)
        layout.add_widget(RadioButtons([("Option 1", 1),
                                        ("Option 2", 2),
                                        ("Option 3", 3)],
                                       label="A Longer Selection:",
                                       name="Things",
                                       on_change=self._on_change), 1)
        layout.add_widget(CheckBox("Field 1",
                                   label="A very silly long name for fields:",
                                   name="CA",
                                   on_change=self._on_change), 1)
        layout.add_widget(
            CheckBox("Field 2", name="CB", on_change=self._on_change), 1)
        layout.add_widget(
            CheckBox("Field 3", name="CC", on_change=self._on_change), 1)
        layout.add_widget(Divider(height=3), 1)
        layout2 = Layout([1, 1, 1])
        self.add_layout(layout2)
        layout2.add_widget(self._reset_button, 0)
        layout2.add_widget(Button("View Data", self._view), 1)
        layout2.add_widget(Button("Quit", self._quit), 2)
        self.fix()

    def _on_change(self):
        changed = False
        self.save()
        for key, value in self.data.items():
            if isinstance(value, bool):
                if value:
                    changed = True
                    break
            if isinstance(value, int):
                if value != 1:
                    changed = True
                    break
            elif value is None:
                continue
            elif len(value) != 0:
                changed = True
                break
        self._reset_button.disabled = not changed

    def _reset(self):
        self.reset()
        raise NextScene()

    def _view(self):
        # Build result of this form and display it.
        self.save()
        raise NextScene()

    @staticmethod
    def _quit():
        raise StopApplication("User requested exit")


class TestFrame2(Frame):
    def __init__(self, screen, init_values):
        super(TestFrame2, self).__init__(screen,
                                         screen.height,
                                         screen.width,
                                         title="Test Frame 2")
        # Create the form for displaying the list of contacts.
        self._list_view = ListBox(
            Widget.FILL_FRAME,
            init_values,
            name="contacts",
            on_change=self._on_pick)
        self._edit_button = Button("Edit", self._edit)
        self._delete_button = Button("Delete", self._delete)
        layout = Layout([100], fill_frame=True)
        self.add_layout(layout)
        layout.add_widget(self._list_view)
        layout.add_widget(Divider())
        layout2 = Layout([1, 1, 1, 1])
        self.add_layout(layout2)
        layout2.add_widget(Button("Add", self._add), 0)
        layout2.add_widget(self._edit_button, 1)
        layout2.add_widget(self._delete_button, 2)
        layout2.add_widget(Button("Quit", self._quit), 3)
        self.fix()
        self._on_pick()

    def _on_pick(self):
        self._edit_button.disabled = self._list_view.value is None
        self._delete_button.disabled = self._list_view.value is None

    @staticmethod
    def _add():
        raise NextScene("Add")

    @staticmethod
    def _edit():
        raise NextScene("Edit")

    @staticmethod
    def _delete():
        raise NextScene("Delete")

    @staticmethod
    def _quit():
        raise StopApplication("User pressed quit")


class TestFrame3(Frame):
    def __init__(self, screen):
        super(TestFrame3, self).__init__(screen, 10, 20,
                                         name="Blank",
                                         has_shadow=True)
        self.fix()


class TestWidgets(unittest.TestCase):
    # see test_focus_callback
    _did_blur = False
    _did_focus = False

    def assert_canvas_equals(self, canvas, expected):
        """
        Assert output to canvas is as expected.
        """
        output = ""
        for y in range(canvas.height):
            for x in range(canvas.width):
                char, _, _, _ = canvas.get_from(x, y)
                output += chr(char)
            output += "\n"
        self.assertEqual(output, expected)

    @staticmethod
    def process_keys(form, values, separator=None):
        """
        Inject a set of key events separated by a common key separator.
        """
        for new_value in values:
            if isinstance(new_value, int):
                form.process_event(KeyboardEvent(new_value))
            else:
                for char in new_value:
                    form.process_event(KeyboardEvent(ord(char)))
            if separator:
                form.process_event(KeyboardEvent(separator))

    @staticmethod
    def process_mouse(form, values):
        """
        Inject a set of mouse events.
        """
        for x, y, buttons in values:
            form.process_event(MouseEvent(x, y, buttons))

    def test_form_data(self):
        """
        Check Frame.data works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)

        # Should be empty on construction
        self.assertEqual(form.data, {})

        # Should be blank values after saving.
        form.reset()
        form.save()
        self.assertEqual(
            form.data,
            {
                'CA': False,
                'CB': False,
                'CC': False,
                'TA': [''],
                'TB': '',
                'TC': '',
                'TD': '',
                'Things': 1
            })

    def test_rendering(self):
        """
        Check Frame renders as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check initial rendering
        form.update(0)
        self.assert_canvas_equals(
            canvas,
            "+--------------------------------------+\n" +
            "| Group 1:                             |\n" +
            "| My First                             O\n" +
            "| Box:                                 |\n" +
            "|                                      |\n" +
            "|                                      |\n" +
            "|                                      |\n" +
            "| Text1:                               |\n" +
            "| Text2:                               |\n" +
            "+--------------------------------------+\n")

        # Check scrolling works.  Should also test label splitting and ellipsis.
        form.move_to(0, 9, 8)
        form.update(1)
        self.assert_canvas_equals(
            canvas,
            "+--------------------------------------+\n" +
            "| Text3:                               |\n" +
            "|                                      |\n" +
            "| ----------------------------------   |\n" +
            "| Group 2:                             O\n" +
            "| A Longer   (X) Option 1              |\n" +
            "| Selection: ( ) Option 2              |\n" +
            "|            ( ) Option 3              |\n" +
            "| A very...  [ ] Field 1               |\n" +
            "+--------------------------------------+\n")

        # Now check button rendering.
        form.move_to(0, 18, 8)
        form.update(2)
        self.assert_canvas_equals(
            canvas,
            "+--------------------------------------+\n" +
            "|            [ ] Field 3               |\n" +
            "|                                      |\n" +
            "| ----------------------------------   |\n" +
            "|                                      |\n" +
            "| < Reset >  < View Data > < Quit >    |\n" +
            "|                                      |\n" +
            "|                                      O\n" +
            "|                                      |\n" +
            "+--------------------------------------+\n")

    def test_unicode_rendering(self):
        """
        Check Frame renders as expected for unicode environments.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=True)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check initial rendering
        form.update(0)
        self.assert_canvas_equals(
            canvas,
            "┌──────────────────────────────────────┐\n" +
            "│ Group 1:                             │\n" +
            "│ My First                             █\n" +
            "│ Box:                                 ░\n" +
            "│                                      ░\n" +
            "│                                      ░\n" +
            "│                                      ░\n" +
            "│ Text1:                               ░\n" +
            "│ Text2:                               │\n" +
            "└──────────────────────────────────────┘\n")

        # Check scrolling works.  Should also test label splitting and ellipsis.
        form.move_to(0, 9, 8)
        form.update(1)
        self.assert_canvas_equals(
            canvas,
            "┌──────────────────────────────────────┐\n" +
            "│ Text3:                               │\n" +
            "│                                      ░\n" +
            "│ ──────────────────────────────────   ░\n" +
            "│ Group 2:                             █\n" +
            "│ A Longer   (•) Option 1              ░\n" +
            "│ Selection: ( ) Option 2              ░\n" +
            "│            ( ) Option 3              ░\n" +
            "│ A very...  [ ] Field 1               │\n" +
            "└──────────────────────────────────────┘\n")

        # Now check button rendering.
        form.move_to(0, 18, 8)
        form.update(2)
        self.assert_canvas_equals(
            canvas,
            "┌──────────────────────────────────────┐\n" +
            "│            [ ] Field 3               │\n" +
            "│                                      ░\n" +
            "│ ──────────────────────────────────   ░\n" +
            "│                                      ░\n" +
            "│ < Reset >  < View Data > < Quit >    ░\n" +
            "│                                      ░\n" +
            "│                                      █\n" +
            "│                                      │\n" +
            "└──────────────────────────────────────┘\n")

    def test_no_border(self):
        """
        Check that a Frame with nor border works
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas, has_border=False)
        form.reset()

        # Check initial rendering
        form.update(0)
        self.assert_canvas_equals(
            canvas,
            "  Group 1:                              \n" +
            "  My First                              \n" +
            "  Box:                                  \n" +
            "                                        \n" +
            "                                        \n" +
            "                                        \n" +
            "  Text1:                                \n" +
            "  Text2:                                \n" +
            "  Text3:                                \n" +
            "                                        \n")

        # Check scrolling works.  Should also test label splitting and ellipsis.
        form.move_to(0, 10, 10)
        form.update(1)
        self.assert_canvas_equals(
            canvas,
            "  ------------------------------------  \n" +
            "  Group 2:                              \n" +
            "  A Longer    (X) Option 1              \n" +
            "  Selection:  ( ) Option 2              \n" +
            "              ( ) Option 3              \n" +
            "  A very si...[ ] Field 1               \n" +
            "              [ ] Field 2               \n" +
            "              [ ] Field 3               \n" +
            "                                        \n" +
            "  ------------------------------------  \n")

    def test_form_input(self):
        """
        Check Frame input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()
        self.process_keys(form,
                          ["ABC\nDEF", "GHI", "jkl", "MN", " ", " ", "", " "],
                          Screen.KEY_TAB)
        form.save()
        self.assertEqual(
            form.data,
            {
                'CA': True,
                'CB': False,
                'CC': True,
                'TA': ['ABC', 'DEF'],
                'TB': 'GHI',
                'TC': 'jkl',
                'TD': 'MN',
                'Things': 1
            })

        # Check that forms ignore unrecognised events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_textbox_input(self):
        """
        Check TextBox input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check basic movement keys
        self.process_keys(form,  ["ABC", Screen.KEY_LEFT, "D"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDC"])
        self.process_keys(form,  [Screen.KEY_RIGHT, Screen.KEY_RIGHT, "E"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDCE"])
        self.process_keys(form,  ["\nFGH", Screen.KEY_UP, Screen.KEY_UP, "I"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "FGH"])
        self.process_keys(form,  [Screen.KEY_DOWN, Screen.KEY_DOWN, "J"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "FGHJ"])
        self.process_keys(form,  [Screen.KEY_HOME, "K"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJ"])
        self.process_keys(form,  [Screen.KEY_END, "L"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJL"])

        # Backspace - normal and wrapping lines
        self.process_keys(form,  [Screen.KEY_BACK])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJ"])
        self.process_keys(form,  [Screen.KEY_HOME, Screen.KEY_BACK])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEKFGHJ"])

        # Check cursor line-wrapping
        self.process_keys(form,  ["\n"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICE", "KFGHJ"])
        self.process_keys(form,  [Screen.KEY_LEFT, "M"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEM", "KFGHJ"])
        self.process_keys(form,  [Screen.KEY_RIGHT, "N"])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEM", "NKFGHJ"])

        # Delete - normal and wrapping lines and at end of all data.
        self.process_keys(form,  [Screen.KEY_DELETE])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEM", "NFGHJ"])
        self.process_keys(form,
                          [Screen.KEY_UP, Screen.KEY_END, Screen.KEY_DELETE])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEMNFGHJ"])
        self.process_keys(form, [Screen.KEY_END, Screen.KEY_DELETE])
        form.save()
        self.assertEqual(form.data["TA"], ["ABDICEMNFGHJ"])

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_text_input(self):
        """
        Check Text input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check basic movement keys
        self.process_keys(form,  [Screen.KEY_TAB, "ABC"])
        form.save()
        self.assertEqual(form.data["TB"], "ABC")
        self.process_keys(form,  [Screen.KEY_HOME, "D"])
        form.save()
        self.assertEqual(form.data["TB"], "DABC")
        self.process_keys(form,  [Screen.KEY_END, "E"])
        form.save()
        self.assertEqual(form.data["TB"], "DABCE")
        self.process_keys(form,  [Screen.KEY_LEFT, "F"])
        form.save()
        self.assertEqual(form.data["TB"], "DABCFE")
        self.process_keys(form,  [Screen.KEY_RIGHT, "G"])
        form.save()
        self.assertEqual(form.data["TB"], "DABCFEG")

        # Backspace
        self.process_keys(form,  [Screen.KEY_BACK])
        form.save()
        self.assertEqual(form.data["TB"], "DABCFE")

        # Delete - including at end of data
        self.process_keys(form,  [Screen.KEY_DELETE])
        form.save()
        self.assertEqual(form.data["TB"], "DABCFE")
        self.process_keys(form,  [Screen.KEY_HOME, Screen.KEY_DELETE])
        form.save()
        self.assertEqual(form.data["TB"], "ABCFE")

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_validation(self):
        """
        Check free-form text validation works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check that save still works with no validation.
        self.process_keys(form,  [Screen.KEY_TAB, Screen.KEY_TAB, "ABC"])
        form.save()
        self.assertEqual(form.data["TC"], "ABC")

        # Check that enforced validation throws exceptions as needed.
        with self.assertRaises(InvalidFields) as cm:
            form.save(validate=True)
        self.assertEqual(cm.exception.fields, ["TC"])

        # Check valid data doesn't throw anything.
        self.process_keys(form,
                          [Screen.KEY_BACK, Screen.KEY_BACK, Screen.KEY_BACK])
        form.save(validate=True)

        # Check functions work as well as regexp strings.
        self.process_keys(form, [Screen.KEY_TAB, "ABC"])
        with self.assertRaises(InvalidFields) as cm:
            form.save(validate=True)
        self.assertEqual(cm.exception.fields, ["TD"])

    def test_checkbox_input(self):
        """
        Check Checkbox input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check basic selection keys
        self.process_keys(
            form,
            [Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB,
             Screen.KEY_TAB, " "])
        form.save()
        self.assertEqual(form.data["CA"], True)
        self.process_keys(form, ["\n"])
        form.save()
        self.assertEqual(form.data["CA"], False)
        self.process_keys(form, ["\r"])
        form.save()
        self.assertEqual(form.data["CA"], True)

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_radiobutton_input(self):
        """
        Check RadioButton input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check basic selection keys - including limit checking
        self.process_keys(
            form,
            [Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB])
        form.save()
        self.assertEqual(form.data["Things"], 1)
        self.process_keys(form, [Screen.KEY_UP])
        form.save()
        self.assertEqual(form.data["Things"], 1)
        self.process_keys(form, [Screen.KEY_DOWN])
        form.save()
        self.assertEqual(form.data["Things"], 2)
        self.process_keys(form, [Screen.KEY_DOWN])
        form.save()
        self.assertEqual(form.data["Things"], 3)
        self.process_keys(form, [Screen.KEY_DOWN])
        form.save()
        self.assertEqual(form.data["Things"], 3)
        self.process_keys(form, [Screen.KEY_UP])
        form.save()
        self.assertEqual(form.data["Things"], 2)

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_mouse_input(self):
        """
        Check mouse input works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        scene = MagicMock(spec=Scene)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.register_scene(scene)
        form.reset()

        # Check focus moves when clicked on a text or textbox
        self.process_mouse(form, [(29, 7, MouseEvent.LEFT_CLICK)])
        self.assertEqual(form._layouts[form._focus]._live_widget, 2)
        self.process_mouse(form, [(29, 2, MouseEvent.LEFT_CLICK)])
        self.assertEqual(form._layouts[form._focus]._live_widget, 1)

        # Check focus doesn't change on scroll or click outside of widgets
        self.process_mouse(form, [(0, 0, 0)])
        self.assertEqual(form._focus, 0)
        self.assertEqual(form._layouts[form._focus]._live_col, 1)
        self.assertEqual(form._layouts[form._focus]._live_widget, 1)
        self.process_mouse(form, [(39, 7, MouseEvent.LEFT_CLICK)])
        self.assertEqual(form._layouts[form._focus]._live_widget, 1)

        # Check focus moves when clicked on a checkbox or radiobutton
        self.process_mouse(form, [(29, 1, MouseEvent.LEFT_CLICK)])
        self.assertEqual(form._layouts[form._focus]._live_widget, 7)
        # Note that the above changes the Frame start-line.
        self.process_mouse(form, [(29, 5, MouseEvent.LEFT_CLICK)])
        self.assertEqual(form._layouts[form._focus]._live_widget, 9)

        # Check focus moves when hovering over a widget
        self.process_mouse(form, [(39, 7, MouseEvent.LEFT_CLICK), (3, 8, 0)])
        self.assertEqual(form._focus, 1)
        self.assertEqual(form._layouts[form._focus]._live_col, 0)
        self.assertEqual(form._layouts[form._focus]._live_widget, 0)

        # Check button click triggers an event.
        with self.assertRaises(StopApplication):
            self.process_mouse(form, [(30, 8, MouseEvent.LEFT_CLICK)])

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def test_widget_navigation(self):
        """
        Check widget tab stops work as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame(canvas)
        form.reset()

        # Check default focus at start is first visible widget
        self.assertEqual(form._focus, 0)
        self.assertEqual(form._layouts[form._focus]._live_col, 1)
        self.assertEqual(form._layouts[form._focus]._live_widget, 1)

        # Check BACK_TAB reverses TAB.
        self.process_keys(form, [Screen.KEY_TAB])
        self.assertEqual(form._layouts[form._focus]._live_widget, 2)
        self.process_keys(form, [Screen.KEY_BACK_TAB])
        self.assertEqual(form._layouts[form._focus]._live_widget, 1)

        # Check BACK_TAB/TAB wraps around the form.
        self.process_keys(form, [Screen.KEY_BACK_TAB])
        self.assertEqual(form._focus, 1)
        self.process_keys(form, [Screen.KEY_TAB])
        self.assertEqual(form._focus, 0)

        # Tab out into text fields and check UP/DOWN keys.
        self.process_keys(form, [Screen.KEY_TAB, Screen.KEY_DOWN])
        self.assertEqual(form._layouts[form._focus]._live_widget, 3)
        self.process_keys(form, [Screen.KEY_UP])
        self.assertEqual(form._layouts[form._focus]._live_widget, 2)

        # Tab out into buttons and check LEFT/RIGHT keys.
        self.process_keys(form, [Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB,
                                 Screen.KEY_TAB, Screen.KEY_TAB, Screen.KEY_TAB,
                                 Screen.KEY_TAB])
        self.assertEqual(form._focus, 1)
        self.assertEqual(form._layouts[form._focus]._live_col, 1)
        self.assertEqual(form._layouts[form._focus]._live_widget, 0)

        self.process_keys(form, [Screen.KEY_RIGHT])
        self.assertEqual(form._layouts[form._focus]._live_col, 2)
        self.process_keys(form, [Screen.KEY_LEFT])
        self.assertEqual(form._layouts[form._focus]._live_col, 1)

    def test_list_box(self):
        """
        Check ListBox widget works as expected.
        """
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        scene = MagicMock(spec=Scene)
        canvas = Canvas(screen, 10, 40, 0, 0)
        form = TestFrame2(canvas, [("One", 1), ("Two", 2)])
        form.register_scene(scene)
        form.reset()

        # Check we have a default value for our list.
        form.save()
        self.assertEqual(form.data, {"contacts": 1})

        # Check that UP/DOWN change selection.
        self.process_keys(form, [Screen.KEY_DOWN])
        form.save()
        self.assertEqual(form.data, {"contacts": 2})
        self.process_keys(form, [Screen.KEY_UP])
        form.save()
        self.assertEqual(form.data, {"contacts": 1})

        # Check that the listbox is rendered correctly.
        form.update(0)
        self.assert_canvas_equals(
            canvas,
            "+------------ Test Frame 2 ------------+\n" +
            "|One                                   |\n" +
            "|Two                                   O\n" +
            "|                                      |\n" +
            "|                                      |\n" +
            "|                                      |\n" +
            "|                                      |\n" +
            "|--------------------------------------|\n" +
            "| < Add > < Edit > < Delete < Quit >   |\n" +
            "+--------------------------------------+\n")

        # Check that mouse input changes selection.
        self.process_mouse(form, [(2, 2, MouseEvent.LEFT_CLICK)])
        form.save()
        self.assertEqual(form.data, {"contacts": 2})
        self.process_mouse(form, [(2, 1, MouseEvent.LEFT_CLICK)])
        form.save()
        self.assertEqual(form.data, {"contacts": 1})

        # Check that the current focus ignores unknown events.
        event = object()
        self.assertEqual(event, form.process_event(event))

    def _on_focus(self):
        self._did_focus = True

    def _on_blur(self):
        self._did_blur = True

    def test_focus_callback(self):
        """
        Check that the _on_focus & _on_blur callbacks work as expected.
        """
        # Create a dummy screen
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        scene = MagicMock(spec=Scene)
        canvas = Canvas(screen, 2, 40, 0, 0)

        # Create the form we want to test.
        form = Frame(canvas, canvas.height, canvas.width, has_border=False)
        layout = Layout([100], fill_frame=True)
        form.add_layout(layout)
        text = Text("Test")
        layout.add_widget(text)
        form.fix()
        # Ensure that none of the callbacks have been called yet.
        self.assertEqual(self._did_blur, False)
        self.assertEqual(self._did_focus, False)
        # Register the callbacks
        text._on_focus = self._on_focus
        text._on_blur = self._on_blur
        form.register_scene(scene)
        form.reset()

        # Blur the Text field
        self.process_keys(form, [Screen.KEY_TAB])
        # Ensure that the callback was called.
        self.assertEqual(self._did_blur, True)

        # Focus the Text field
        self.process_mouse(form, [(1, 20, MouseEvent.LEFT_CLICK)])
        # Ensure that the callback was called.
        self.assertEqual(self._did_focus, True)

    def test_multi_column_list_box(self):
        """
        Check MultiColumnListBox works as expected.
        """
        # Create a dummy screen.
        screen = MagicMock(spec=Screen, colours=8, unicode_aware=False)
        scene = MagicMock(spec=Scene)
        canvas = Canvas(screen, 10, 40, 0, 0)

        # Create the form we want to test.
        form = Frame(canvas, canvas.height, canvas.width, has_border=False)
        layout = Layout([100], fill_frame=True)
        mc_list = MultiColumnListBox(
            Widget.FILL_FRAME,
            [3, "4", ">4", "<4", ">10%", "100%"],
            [
                (["1", "2", "3", "4", "5", "6"], 1),
                (["11", "222", "333", "444", "555", "6"], 2),
                (["111", "2", "3", "4", "5", "6"], 3),
                (["1", "2", "33333", "4", "5", "6"], 4),
                (["1", "2", "3", "4", "5", "6666666666666666666666"], 5),
            ],
            titles=["A", "B", "C", "D", "E", "F"],
            name="mc_list")
        form.add_layout(layout)
        layout.add_widget(mc_list)
{'fraction_non_alphanumeric': 0.14425479103202618, 'fraction_numerical': 0.012700868752316942, 'mean_word_length': 1.45650710694065, 'pattern_counts': {'":': 10, '<': 25, '<?xml version=': 0, '>': 25, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 2, 'word_list_counts': {'cursed_substrings.json': 186, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7111591', 'n_tokens_mistral': 18262, 'n_tokens_neox': 16975, 'n_words': 3740}