1# ------------------------------------------------------------------------------
2#
3#  Copyright (c) 2012, Enthought, Inc.
4#  All rights reserved.
5#
6#  This software is provided without warranty under the terms of the BSD
7#  license included in LICENSE.txt and may be redistributed only
8#  under the conditions described in the aforementioned license.  The license
9#  is also available online at http://www.enthought.com/licenses/BSD.txt
10#
11#  Author: Pietro Berkes
12#  Date:   Feb 2012
13#
14# ------------------------------------------------------------------------------
15
16"""
17Test the creation and layout of labels.
18"""
19import unittest
20
21from traits.has_traits import HasTraits
22from traits.trait_types import Bool, Str
23from traitsui.view import View
24from traitsui.item import Item
25from traitsui.group import VGroup, HGroup
26
27from traitsui.tests._tools import (
28    BaseTestMixin,
29    create_ui,
30    is_control_enabled,
31    is_qt,
32    requires_toolkit,
33    reraise_exceptions,
34    ToolkitName,
35)
36
37
38_DIALOG_WIDTH = 500
39
40
41class ShowRightLabelsDialog(HasTraits):
42    """ Dialog with labels on the left/right to test the label text.
43    """
44
45    bool_item = Bool(True)
46
47    traits_view = View(
48        VGroup(
49            VGroup(Item("bool_item"), show_left=False),
50            VGroup(Item("bool_item"), show_left=True),
51        )
52    )
53
54
55class HResizeTestDialog(HasTraits):
56    """ Dialog with checkbox and text elements and labels on the right.
57    We test the separation between element and label in HGroups.
58    """
59
60    bool_item = Bool(True)
61    txt_item = Str()
62
63    traits_view = View(
64        VGroup(
65            HGroup(Item("bool_item", springy=True), show_left=False),
66            VGroup(Item("txt_item", resizable=True), show_left=False),
67        ),
68        width=_DIALOG_WIDTH,
69        height=100,
70        resizable=True,
71    )
72
73
74class VResizeTestDialog(HasTraits):
75    """ Dialog with checkbox and text elements and labels on the right.
76    We test the separation between element and label in VGroups.
77    """
78
79    bool_item = Bool(True)
80    txt_item = Str()
81
82    traits_view = View(
83        VGroup(
84            VGroup(Item("bool_item", resizable=True), show_left=False),
85            VGroup(Item("txt_item", resizable=True), show_left=False),
86        ),
87        width=_DIALOG_WIDTH,
88        height=100,
89        resizable=True,
90    )
91
92
93class NoLabelResizeTestDialog(HasTraits):
94    """ Test the combination show_label=False, show_left=False.
95    """
96
97    bool_item = Bool(True)
98
99    traits_view = View(
100        VGroup(
101            Item("bool_item", resizable=True, show_label=False),
102            show_left=False,
103        ),
104        resizable=True,
105    )
106
107
108class EnableWhenDialog(HasTraits):
109    """ Test labels for enable when. """
110
111    bool_item = Bool(True)
112
113    labelled_item = Str("test")
114
115    unlabelled_item = Str("test")
116
117    traits_view = View(
118        VGroup(
119            Item("bool_item"),
120            Item("labelled_item", enabled_when="bool_item"),
121            Item(
122                "unlabelled_item", enabled_when="bool_item", show_label=False
123            ),
124        ),
125        resizable=True,
126    )
127
128
129class TestLabels(BaseTestMixin, unittest.TestCase):
130
131    def setUp(self):
132        BaseTestMixin.setUp(self)
133
134    def tearDown(self):
135        BaseTestMixin.tearDown(self)
136
137    @requires_toolkit([ToolkitName.qt])
138    def test_qt_show_labels_right_without_colon(self):
139        # Behavior: traitsui should not append a colon ':' to labels
140        # that are shown to the *right* of the corresponding elements
141
142        from pyface import qt
143        dialog = ShowRightLabelsDialog()
144        with reraise_exceptions(), \
145                create_ui(dialog) as ui:
146
147            # get reference to label objects
148            labels = ui.control.findChildren(qt.QtGui.QLabel)
149
150            # the first is shown to the right, so no colon
151            self.assertFalse(labels[0].text().endswith(":"))
152
153            # the second is shown to the right, it should have a colon
154            self.assertTrue(labels[1].text().endswith(":"))
155
156    def _test_qt_labels_right_resizing(self, dialog_class):
157        # Bug: In the Qt backend, resizing a checkbox element with a label on
158        # the right resizes the checkbox, even though it cannot be.
159        # The final effect is that the label remains attached to the right
160        # margin, with a big gap between it and the checkbox. In this case, the
161        # label should be made resizable instead.
162        # On the other hand, a text element should keep the current behavior
163        # and resize.
164
165        from pyface import qt
166
167        with reraise_exceptions(), \
168                create_ui(dialog_class()) as ui:
169
170            # all labels
171            labels = ui.control.findChildren(qt.QtGui.QLabel)
172
173            # the checkbox and its label should be close to one another; the
174            # size of the checkbox should be small
175            checkbox_label = labels[0]
176            checkbox = ui.control.findChild(qt.QtGui.QCheckBox)
177
178            # horizontal space between checkbox and label should be small
179            h_space = checkbox_label.x() - checkbox.x()
180            self.assertLess(h_space, 100)
181            # and the checkbox size should also be small
182            self.assertLess(checkbox.width(), 100)
183
184            # the text item and its label should be close to one another; the
185            # size of the text item should be large
186            text_label = labels[0]
187            text = ui.control.findChild(qt.QtGui.QLineEdit)
188
189            # horizontal space between text and label should be small
190            h_space = text_label.x() - text.x()
191            self.assertLess(h_space, 100)
192            # and the text item size should be large
193            self.assertGreater(text.width(), _DIALOG_WIDTH - 200)
194
195            # the size of the window should still be 500
196            self.assertEqual(ui.control.width(), _DIALOG_WIDTH)
197
198    @requires_toolkit([ToolkitName.qt])
199    def test_qt_labels_right_resizing_vertical(self):
200        self._test_qt_labels_right_resizing(VResizeTestDialog)
201
202    @requires_toolkit([ToolkitName.qt])
203    def test_qt_labels_right_resizing_horizontal(self):
204        self._test_qt_labels_right_resizing(HResizeTestDialog)
205
206    @requires_toolkit([ToolkitName.qt, ToolkitName.wx])
207    def test_labels_enabled_when(self):
208        # Behaviour: label should enable/disable along with editor
209
210        dialog = EnableWhenDialog()
211        with reraise_exceptions(), \
212                create_ui(dialog) as ui:
213
214            labelled_editor = ui.get_editors("labelled_item")[0]
215
216            if is_qt():
217                unlabelled_editor = ui.get_editors("unlabelled_item")[0]
218                self.assertIsNone(unlabelled_editor.label_control)
219
220            self.assertTrue(is_control_enabled(labelled_editor.label_control))
221
222            dialog.bool_item = False
223
224            self.assertFalse(is_control_enabled(labelled_editor.label_control))
225
226            dialog.bool_item = True
227
228
229@requires_toolkit([ToolkitName.qt, ToolkitName.wx])
230class TestAnyToolkit(BaseTestMixin, unittest.TestCase):
231    """ Toolkit-agnostic tests for labels with different orientations."""
232
233    def setUp(self):
234        BaseTestMixin.setUp(self)
235
236    def tearDown(self):
237        BaseTestMixin.tearDown(self)
238
239    def test_group_show_right_labels(self):
240        with reraise_exceptions(), \
241                create_ui(ShowRightLabelsDialog()):
242            pass
243
244    def test_horizontal_resizable_and_labels(self):
245        with reraise_exceptions(), \
246                create_ui(HResizeTestDialog()):
247            pass
248
249    def test_all_resizable_with_labels(self):
250        with reraise_exceptions(), \
251                create_ui(VResizeTestDialog()):
252            pass
253
254    def test_show_right_with_no_label(self):
255        # Bug: If one set show_left=False, show_label=False on a non-resizable
256        # item like a checkbox, the Qt backend tried to set the label's size
257        # policy and failed because label=None.
258        with reraise_exceptions(), \
259                create_ui(NoLabelResizeTestDialog()):
260            pass
261
262    def test_enable_when_flag(self):
263        with reraise_exceptions(), \
264                create_ui(EnableWhenDialog()):
265            pass
266
267
268if __name__ == "__main__":
269    # Execute from command line for manual testing
270    vw = HResizeTestDialog()
271    vw.configure_traits()
272