diff --git a/enable/qt4/scrollbar.py b/enable/qt4/scrollbar.py index f8cd4fe0e..79a048ebe 100644 --- a/enable/qt4/scrollbar.py +++ b/enable/qt4/scrollbar.py @@ -178,20 +178,46 @@ def _create_control(self, window, enable_range, value): def _update_control(self, enable_range, value): minimum, maximum, page_size, line_size = enable_range - self._control.setMinimum(minimum) # The maximum value of a QScrollBar is the maximum position of the # scroll bar, not the document length. We need to subtract the length # of the scroll bar itself. - self._control.setMaximum(maximum-page_size) + max_value = maximum-page_size + # invert values for vertical ranges because of coordinate system issues + value = self._correct_value(value, minimum, max_value) + + self._control.setMinimum(minimum) + self._control.setMaximum(max_value) self._control.setValue(value) self._control.setPageStep(page_size) self._control.setSingleStep(line_size) + def _correct_value(self, value, min_value, max_value): + """ Correct vertical position values for Qt and Enable conventions + + Enable expects vertical scroll_position to be measured with origin at + the bottom and positive going upwards, while Qt scrollbar values are + measured with origin at the top and positive going down. + + Parameters + ---------- + value : float + The position value in either Enable or Qt conventions. + max_value : float + The maximum value that the Qt scrollbar can be set to (height of + the scrolled component, less the page size). + """ + if self.orientation != 'vertical': + return value + return max_value - (value - min_value) + + #------------------------------------------------------------------------ # Qt Event handlers #------------------------------------------------------------------------ def _update_enable_pos(self, value): + # invert values for vertical ranges because of coordinate system issues + value = self._correct_value(value, self.low, self.high-self.page_size) self.scroll_position = value def _on_slider_pressed(self): @@ -285,4 +311,3 @@ def _set_line_size(self, line_size): low, high, page_size, ignore = self.range self._clean = False self.range =(low, high, page_size, line_size) - diff --git a/enable/tests/qt4/__init__.py b/enable/tests/qt4/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/enable/tests/qt4/scrollbar_test.py b/enable/tests/qt4/scrollbar_test.py new file mode 100644 index 000000000..93600fd58 --- /dev/null +++ b/enable/tests/qt4/scrollbar_test.py @@ -0,0 +1,144 @@ + +from contextlib import contextmanager + +from traits.testing.unittest_tools import unittest +from traitsui.tests._tools import skip_if_not_qt4 +from pyface.gui import GUI + +from enable.container import Container +from enable.window import Window + +try: + from enable.qt4.scrollbar import NativeScrollBar +except Exception: + NativeScrollBar = None + + +@skip_if_not_qt4 +class ScrollBarTest(unittest.TestCase): + + def setUp(self): + if NativeScrollBar is None: + raise unittest.SkipTest("Qt4 NativeScrollbar not available.") + self.gui = GUI() + self.container = Container(position=[0, 0], bounds=[600, 600]) + self.window = Window(None, size=(600, 600), component=self.container) + + @contextmanager + def setup_window(self, window): + window.control.show() + window._size = window._get_control_size() + self.gui.process_events() + try: + yield + finally: + window.control.destroy() + + @contextmanager + def setup_scrollbar(self, scrollbar, window): + scrollbar._draw_mainlayer(window._gc) + try: + yield + finally: + scrollbar.destroy() + + def test_scroll_position_horizontal(self): + bounds = [600.0, 30.0] + position = [0.0, 0.0] + range = [600, 0.0, 375.0, 20.454545454545453] + scrollbar = NativeScrollBar( + orientation='horizontal', + bounds=bounds, + position=position, + range=range, + ) + self.container.add(scrollbar) + with self.setup_window(self.window): + with self.setup_scrollbar(scrollbar, self.window): + self.assertEqual(scrollbar._control.value(), 0) + self.assertEqual(scrollbar.scroll_position, 0) + + # move the scrollbar + scrollbar._control.setValue(100) + self.assertEqual(scrollbar.scroll_position, 100) + + # set the scroll & redraw + scrollbar.scroll_position = 200 + scrollbar._draw_mainlayer(self, self.window._gc) + self.assertEqual(scrollbar._control.value(), 200) + + def test_scroll_position_vertical(self): + bounds = [30.0, 600.0] + position = [0.0, 0.0] + range = [600, 0.0, 375.0, 20.454545454545453] + scrollbar = NativeScrollBar( + orientation='vertical', + bounds=bounds, + position=position, + range=range, + ) + self.container.add(scrollbar) + with self.setup_window(self.window): + with self.setup_scrollbar(scrollbar, self.window): + self.assertEqual(scrollbar._control.value(), 600-375) + self.assertEqual(scrollbar.scroll_position, 0) + + # move the scrollbar + scrollbar._control.setValue(100) + self.assertEqual(scrollbar.scroll_position, 600-375-100) + + # set the scroll & redraw + scrollbar.scroll_position = 200 + scrollbar._draw_mainlayer(self, self.window._gc) + self.assertEqual(scrollbar._control.value(), 600-375-200) + + def test_minumum_horizontal(self): + bounds = [600.0, 30.0] + position = [0.0, 0.0] + range = [700, 100.0, 375.0, 20.454545454545453] + scrollbar = NativeScrollBar( + orientation='horizontal', + bounds=bounds, + position=position, + range=range, + ) + self.container.add(scrollbar) + with self.setup_window(self.window): + with self.setup_scrollbar(scrollbar, self.window): + self.assertEqual(scrollbar._control.value(), 100) + self.assertEqual(scrollbar.scroll_position, 100) + + # move the scrollbar + scrollbar._control.setValue(200) + self.assertEqual(scrollbar.scroll_position, 200) + + # set the scroll & redraw + scrollbar.scroll_position = 300 + scrollbar._draw_mainlayer(self, self.window._gc) + self.assertEqual(scrollbar._control.value(), 300) + + def test_minimum_vertical(self): + bounds = [30.0, 600.0] + position = [0.0, 0.0] + range = [700, 100.0, 375.0, 20.454545454545453] + scrollbar = NativeScrollBar( + orientation='vertical', + bounds=bounds, + position=position, + range=range, + ) + self.container.add(scrollbar) + with self.setup_window(self.window): + with self.setup_scrollbar(scrollbar, self.window): + # control should be at top + self.assertEqual(scrollbar._control.value(), 700-375) + self.assertEqual(scrollbar.scroll_position, 100) + + # move the scrollbar to the bottom + scrollbar._control.setValue(100) + self.assertEqual(scrollbar.scroll_position, 700-375) + + # set the scroll & redraw + scrollbar.scroll_position = 200 + scrollbar._draw_mainlayer(self, self.window._gc) + self.assertEqual(scrollbar._control.value(), 700-375-(200-100))