Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add unified dragging API to UI elements #635

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
69 changes: 13 additions & 56 deletions fury/ui/containers.py
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ def __init__(self, size, position=(0, 0), color=(0.1, 0.1, 0.1),
self.has_border = has_border
self._border_color = border_color
self._border_width = border_width
super(Panel2D, self).__init__(position)
super(Panel2D, self).__init__(position, draggable=True)
self.resize(size)
self.alignment = align
self.color = color
Expand Down Expand Up @@ -84,18 +84,12 @@ def _setup(self):
self.borders[key].color = self._border_color
self.add_element(self.borders[key], self.border_coords[key])

for key in self.borders.keys():
self.borders[key].on_left_mouse_button_pressed = \
self.left_button_pressed

self.borders[key].on_left_mouse_button_dragged = \
self.left_button_dragged

self.add_element(self.background, (0, 0))

# Add default events listener for this UI component.
self.background.on_left_mouse_button_pressed = self.left_button_pressed
self.background.on_left_mouse_button_dragged = self.left_button_dragged
self.set_draggable_components(
self.background,
*self.borders.values() if self.has_border else [],
boundary_component=self
)

def _get_actors(self):
"""Get the actors composing this UI component."""
Expand Down Expand Up @@ -239,18 +233,6 @@ def update_element(self, element, coords, anchor="position"):
self.remove_element(element)
self.add_element(element, coords, anchor)

def left_button_pressed(self, i_ren, _obj, panel2d_object):
click_pos = np.array(i_ren.event.position)
self._drag_offset = click_pos - self.position
i_ren.event.abort() # Stop propagating the event.

def left_button_dragged(self, i_ren, _obj, _panel2d_object):
if self._drag_offset is not None:
click_position = np.array(i_ren.event.position)
new_position = click_position - self._drag_offset
self.position = new_position
i_ren.force_render()

def re_align(self, window_size_change):
"""Re-organise the elements in case the window size is changed.

Expand Down Expand Up @@ -546,7 +528,7 @@ def __init__(self, position=(0, 0), size=(100, 100), nb_tabs=1,
self.active_tab_idx = None
self.collapsed = True

super(TabUI, self).__init__()
super(TabUI, self).__init__(draggable=draggable)
self.position = position

def _setup(self):
Expand Down Expand Up @@ -614,25 +596,12 @@ def update_tabs(self):
tab_panel.content_panel.position = self.position

content_panel = tab_panel.content_panel
if self.draggable:
tab_panel.panel.background.on_left_mouse_button_pressed =\
self.left_button_pressed
content_panel.background.on_left_mouse_button_pressed =\
self.left_button_pressed
tab_panel.text_block.on_left_mouse_button_pressed =\
self.left_button_pressed

tab_panel.panel.background.on_left_mouse_button_dragged =\
self.left_button_dragged
content_panel.background.on_left_mouse_button_dragged =\
self.left_button_dragged
tab_panel.text_block.on_left_mouse_button_dragged =\
self.left_button_dragged
else:
tab_panel.panel.background.on_left_mouse_button_dragged =\
lambda i_ren, _obj, _comp: i_ren.force_render
content_panel.background.on_left_mouse_button_dragged =\
lambda i_ren, _obj, _comp: i_ren.force_render
self.set_draggable_components(
tab_panel.panel.background,
content_panel.background,
tab_panel.text_block,
boundary_component=self.parent_panel
)

tab_panel.text_block.on_left_mouse_button_clicked =\
self.select_tab_callback
Expand Down Expand Up @@ -702,18 +671,6 @@ def update_element(self, tab_idx, element, coords, anchor="position"):
raise IndexError("Tab with index "
"{} does not exist".format(tab_idx))

def left_button_pressed(self, i_ren, _obj, _sub_component):
click_pos = np.array(i_ren.event.position)
self._click_position = click_pos
i_ren.event.abort() # Stop propagating the event.

def left_button_dragged(self, i_ren, _obj, _sub_component):
click_position = np.array(i_ren.event.position)
change = click_position - self._click_position
self.parent_panel.position += change
self._click_position = click_position
i_ren.force_render()


class ImageContainer2D(UI):
"""A 2D container to hold an image.
Expand Down
50 changes: 49 additions & 1 deletion fury/ui/core.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ class UI(object, metaclass=abc.ABCMeta):

"""

def __init__(self, position=(0, 0)):
def __init__(self, position=(0, 0), draggable=False):
"""Init scene.

Parameters
Expand All @@ -84,6 +84,9 @@ def __init__(self, position=(0, 0)):
self._scene = object()
self._position = np.array([0, 0])
self._callbacks = []
self._draggable = draggable
self._draggable_components = []
self._boundary_component = None

self._setup() # Setup needed actors and sub UI components.
self.position = position
Expand All @@ -108,6 +111,37 @@ def __init__(self, position=(0, 0)):
self.on_middle_mouse_double_clicked = lambda i_ren, obj, element: None
self.on_middle_mouse_button_dragged = lambda i_ren, obj, element: None
self.on_key_press = lambda i_ren, obj, element: None
if self._boundary_component is None and self._draggable:
raise ValueError("UI component was meant to be draggable but "
"boundary component was not provided.")
self._set_draggable() # Setup draggable components.

def _set_draggable(self):
""" Hooks draggable components with appropriate methods."""
for component in self._draggable_components:
if self._draggable:
component.on_left_mouse_button_dragged =\
self._left_button_dragged
component.on_left_mouse_button_pressed =\
self._left_button_pressed
else:
component.on_left_mouse_button_dragged = \
lambda i_ren, _obj, _comp: i_ren.force_render
component.on_left_mouse_button_pressed = \
lambda i_ren, _obj, _comp: i_ren.force_render

def set_draggable_components(self, *components, boundary_component):
"""
Parameters
----------
components: [UI, UI, ...]
List of draggable subcomponents.
boundary_component: UI
The boundary or the parent component which encapsulates the
said subcomponents. In most cases it's an instance of Panel2D.
"""
self._draggable_components.extend(components)
self._boundary_component = boundary_component

@abc.abstractmethod
def _setup(self):
Expand Down Expand Up @@ -328,6 +362,20 @@ def mouse_move_callback(i_ren, obj, self):
def key_press_callback(i_ren, obj, self):
self.on_key_press(i_ren, obj, self)

def _left_button_pressed(self, i_ren, _obj, _sub_component):
""" Method to felicitate dragging while a component is pressed."""
click_pos = np.array(i_ren.event.position)
self._click_position = click_pos
i_ren.event.abort() # Stop propagating the event.

def _left_button_dragged(self, i_ren, _obj, _sub_component):
""" Method to felicitate dragging while a component is dragged."""
click_position = np.array(i_ren.event.position)
change = click_position - self._click_position
self._boundary_component.position += change
self._click_position = click_position
i_ren.force_render()


class Rectangle2D(UI):
"""A 2D rectangle sub-classed from UI."""
Expand Down
45 changes: 8 additions & 37 deletions fury/ui/elements.py
Original file line number Diff line number Diff line change
Expand Up @@ -2081,7 +2081,6 @@ def __init__(self, items=[], position=(0, 0), size=(300, 200),
self._selection = placeholder
self._menu_visibility = False
self._selection_ID = None
self.draggable = draggable
self.sel_text_color = selection_text_color
self.sel_bg_color = selection_bg_color
self.menu_txt_color = menu_text_color
Expand All @@ -2100,7 +2099,7 @@ def __init__(self, items=[], position=(0, 0), size=(300, 200),
('left', read_viz_icons(fname='circle-left.png')),
('down', read_viz_icons(fname='circle-down.png'))]

super(ComboBox2D, self).__init__()
super(ComboBox2D, self).__init__(draggable=draggable)
self.position = position

def _setup(self):
Expand Down Expand Up @@ -2134,29 +2133,13 @@ def _setup(self):
self.panel.add_element(self.drop_down_button, (0.8, 0.7))
self.panel.add_element(self.drop_down_menu, (0, 0))

if self.draggable:
self.drop_down_button.on_left_mouse_button_dragged =\
self.left_button_dragged
self.drop_down_menu.panel.background.on_left_mouse_button_dragged\
= self.left_button_dragged
self.selection_box.on_left_mouse_button_dragged =\
self.left_button_dragged
self.selection_box.background.on_left_mouse_button_dragged =\
self.left_button_dragged

self.drop_down_button.on_left_mouse_button_pressed =\
self.left_button_pressed
self.drop_down_menu.panel.background.on_left_mouse_button_pressed\
= self.left_button_pressed
self.selection_box.on_left_mouse_button_pressed =\
self.left_button_pressed
self.selection_box.background.on_left_mouse_button_pressed =\
self.left_button_pressed
else:
self.panel.background.on_left_mouse_button_dragged =\
lambda i_ren, _obj, _comp: i_ren.force_render
self.drop_down_menu.panel.background.on_left_mouse_button_dragged\
= lambda i_ren, _obj, _comp: i_ren.force_render
self.set_draggable_components(
self.drop_down_button,
self.drop_down_menu.panel.background,
self.selection_box,
self.selection_box.background,
boundary_component=self.panel
)

# Handle mouse wheel events on the slots.
for slot in self.drop_down_menu.slots:
Expand Down Expand Up @@ -2303,18 +2286,6 @@ def menu_toggle_callback(self, i_ren, _vtkactor, _combobox):
i_ren.force_render()
i_ren.event.abort() # Stop propagating the event.

def left_button_pressed(self, i_ren, _obj, _sub_component):
click_pos = np.array(i_ren.event.position)
self._click_position = click_pos
i_ren.event.abort() # Stop propagating the event.

def left_button_dragged(self, i_ren, _obj, _sub_component):
click_position = np.array(i_ren.event.position)
change = click_position - self._click_position
self.panel.position += change
self._click_position = click_position
i_ren.force_render()


class ListBox2D(UI):
"""UI component that allows the user to select items from a list.
Expand Down
1 change: 0 additions & 1 deletion fury/ui/tests/test_helpers.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
"""Test helpers fonction ."""
import numpy as np
import numpy.testing as npt

Expand Down