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

UI: Adding Bounding Box & Fixing Alignment issue in TextBlock2D #803

Merged
merged 26 commits into from Jul 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
26 commits
Select commit Hold shift + click to select a range
9a373a1
adding bounding box property
ganimtron-10 Jun 12, 2023
e35ae2c
seperating bounding box function
ganimtron-10 Jun 12, 2023
868a4e4
updating bbox on message change
ganimtron-10 Jun 12, 2023
9bbe987
removing comments
ganimtron-10 Jun 12, 2023
08dcc24
updating alignment
ganimtron-10 Jun 15, 2023
67867f6
updating bbox on resize
ganimtron-10 Jun 15, 2023
b703f0a
adding bounding box and updating alignment
ganimtron-10 Jul 5, 2023
1206b48
adding auto_font_scale property to scale font automatically
ganimtron-10 Jul 5, 2023
2b5e6c2
updating the calculation of the bounding box size
ganimtron-10 Jul 5, 2023
7648da0
updating tests
ganimtron-10 Jul 5, 2023
2e268d3
adding docs
ganimtron-10 Jul 5, 2023
3cae30e
updating aligment calculation
ganimtron-10 Jul 20, 2023
c95b249
avoiding repetative calculation
ganimtron-10 Jul 20, 2023
acb3ad4
removing test changes
ganimtron-10 Jul 20, 2023
3523a0c
adding dynamic_bbox property
ganimtron-10 Jul 21, 2023
537a3f0
reseting size to zero
ganimtron-10 Jul 21, 2023
6cf858d
updating tests
ganimtron-10 Jul 21, 2023
5c9bf87
updating docs
ganimtron-10 Jul 22, 2023
0f1d6c3
updating docs and initializing param
ganimtron-10 Jul 24, 2023
09189f1
updating font size test
ganimtron-10 Jul 26, 2023
4d01700
updating case checking
ganimtron-10 Jul 26, 2023
483ea64
updating size value
ganimtron-10 Jul 28, 2023
9b23d2d
updating helper
ganimtron-10 Jul 28, 2023
0f47c83
updating helper tests
ganimtron-10 Jul 28, 2023
aa56657
updating size for tests
ganimtron-10 Jul 28, 2023
0ecdc84
removing attributes
ganimtron-10 Jul 29, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
185 changes: 104 additions & 81 deletions fury/ui/core.py
Expand Up @@ -701,6 +701,8 @@
color=(1, 1, 1),
bg_color=None,
position=(0, 0),
auto_font_scale=False,
dynamic_bbox=False
):
"""Init class instance.

Expand Down Expand Up @@ -730,23 +732,34 @@
Adds text shadow.
size : (int, int)
Size (width, height) in pixels of the text bounding box.
auto_font_scale : bool, optional
Automatically scale font according to the text bounding box.
dynamic_bbox : bool, optional
Automatically resize the bounding box according to the content.
"""
self.boundingbox = [0, 0, 0, 0]
super(TextBlock2D, self).__init__(position=position)
self.scene = None
self.have_bg = bool(bg_color)
if size is not None:
self.resize(size)
else:
self.font_size = font_size
self.color = color
self.background_color = bg_color
self.font_family = font_family
self.justification = justification
self._justification = justification
self.bold = bold
self.italic = italic
self.shadow = shadow
self.vertical_justification = vertical_justification
self._vertical_justification = vertical_justification
self.auto_font_scale = auto_font_scale
if self.auto_font_scale:
self.actor.SetTextScaleModeToProp()
self.dynamic_bbox = dynamic_bbox
ganimtron-10 marked this conversation as resolved.
Show resolved Hide resolved
self.message = text
self.font_size = font_size
if size is not None:
self.resize(size)
elif not self.dynamic_bbox:
# raise ValueError("TextBlock size is required as it is not dynamic.")
self.resize((0, 0))

def _setup(self):
self.actor = TextActor()
Expand All @@ -762,10 +775,7 @@
size : (int, int)
Text bounding box size(width, height) in pixels.
"""
if self.have_bg:
self.background.resize(size)
self.actor.SetTextScaleModeToProp()
self.actor.SetPosition2(*size)
self.update_bounding_box(size)

def _get_actors(self):
"""Get the actors composing this UI component."""
Expand All @@ -778,11 +788,6 @@
----------
scene : scene
"""
self.scene = scene
if self.have_bg and not self.actor.GetTextScaleMode():
size = np.zeros(2)
self.actor.GetSize(scene, size)
self.background.resize(size)
scene.add(self.background, self.actor)

@property
Expand All @@ -806,6 +811,8 @@
The message to be set.
"""
self.actor.SetInput(text)
if self.dynamic_bbox:
self.update_bounding_box()

@property
def font_size(self):
Expand All @@ -827,21 +834,12 @@
size : int
Text font size.
"""
self.actor.SetTextScaleModeToNone()
self.actor.GetTextProperty().SetFontSize(size)

if self.scene is not None and self.have_bg:
bb_size = np.zeros(2)
self.actor.GetSize(self.scene, bb_size)
bg_size = self.background.size
if bb_size[0] > bg_size[0] or bb_size[1] > bg_size[1]:
warn(
'Font size exceeds background bounding box.'
' Font Size will not be updated.',
RuntimeWarning,
)
self.actor.SetTextScaleModeToProp()
self.actor.SetPosition2(*bg_size)
if not self.auto_font_scale:
self.actor.SetTextScaleModeToNone()
self.actor.GetTextProperty().SetFontSize(size)

if self.dynamic_bbox:
self.update_bounding_box()

@property
def font_family(self):
Expand Down Expand Up @@ -881,13 +879,7 @@
str
Text justification.
"""
justification = self.actor.GetTextProperty().GetJustificationAsString()
if justification == 'Left':
return 'left'
elif justification == 'Centered':
return 'center'
elif justification == 'Right':
return 'right'
return self._justification

@justification.setter
def justification(self, justification):
Expand All @@ -899,16 +891,8 @@
Possible values are left, right, center.

"""
text_property = self.actor.GetTextProperty()
if justification == 'left':
text_property.SetJustificationToLeft()
elif justification == 'center':
text_property.SetJustificationToCentered()
elif justification == 'right':
text_property.SetJustificationToRight()
else:
msg = 'Text can only be justified left, right and center.'
raise ValueError(msg)
self._justification = justification
self.update_alignment()

@property
def vertical_justification(self):
Expand All @@ -920,14 +904,7 @@
Text vertical justification.

"""
text_property = self.actor.GetTextProperty()
vjustification = text_property.GetVerticalJustificationAsString()
if vjustification == 'Bottom':
return 'bottom'
elif vjustification == 'Centered':
return 'middle'
elif vjustification == 'Top':
return 'top'
return self._vertical_justification

@vertical_justification.setter
def vertical_justification(self, vertical_justification):
Expand All @@ -939,16 +916,8 @@
Possible values are bottom, middle, top.

"""
text_property = self.actor.GetTextProperty()
if vertical_justification == 'bottom':
text_property.SetVerticalJustificationToBottom()
elif vertical_justification == 'middle':
text_property.SetVerticalJustificationToCentered()
elif vertical_justification == 'top':
text_property.SetVerticalJustificationToTop()
else:
msg = 'Vertical justification must be: bottom, middle or top.'
raise ValueError(msg)
self._vertical_justification = vertical_justification
self.update_alignment()

@property
def bold(self):
Expand Down Expand Up @@ -1078,6 +1047,71 @@
self.background.set_visibility(True)
self.background.color = color

def update_alignment(self):
"""Update Text Alignment.
"""
text_property = self.actor.GetTextProperty()
updated_text_position = [0, 0]

if self.justification.lower() == 'left':
text_property.SetJustificationToLeft()
updated_text_position[0] = self.boundingbox[0]
elif self.justification.lower() == 'center':
text_property.SetJustificationToCentered()
updated_text_position[0] = self.boundingbox[0] + \
(self.boundingbox[2]-self.boundingbox[0])//2
elif self.justification.lower() == 'right':
text_property.SetJustificationToRight()
updated_text_position[0] = self.boundingbox[2]
else:
msg = 'Text can only be justified left, right and center.'
raise ValueError(msg)

if self.vertical_justification.lower() == 'bottom':
text_property.SetVerticalJustificationToBottom()
updated_text_position[1] = self.boundingbox[1]
elif self.vertical_justification.lower() == 'middle':
text_property.SetVerticalJustificationToCentered()
updated_text_position[1] = self.boundingbox[1] + \
(self.boundingbox[3]-self.boundingbox[1])//2
elif self.vertical_justification.lower() == 'top':
text_property.SetVerticalJustificationToTop()
updated_text_position[1] = self.boundingbox[3]
else:
msg = 'Vertical justification must be: bottom, middle or top.'
raise ValueError(msg)

Check warning on line 1082 in fury/ui/core.py

View check run for this annotation

Codecov / codecov/patch

fury/ui/core.py#L1081-L1082

Added lines #L1081 - L1082 were not covered by tests

self.actor.SetPosition(updated_text_position)

def cal_size_from_message(self):
"Calculate size of background according to the message it contains."
lines = self.message.split("\n")
max_length = max(len(line) for line in lines)
return [max_length*self.font_size, len(lines)*self.font_size]

def update_bounding_box(self, size=None):
"""Update Text Bounding Box.

Parameters
----------
size : (int, int) or None
If None, calculates bounding box.
Otherwise, uses the given size.

"""
if size is None:
size = self.cal_size_from_message()

self.boundingbox = [self.position[0], self.position[1],
ganimtron-10 marked this conversation as resolved.
Show resolved Hide resolved
self.position[0]+size[0], self.position[1]+size[1]]
self.background.resize(size)

if self.auto_font_scale:
self.actor.SetPosition2(
self.boundingbox[2]-self.boundingbox[0], self.boundingbox[3]-self.boundingbox[1])
else:
self.update_alignment()

def _set_position(self, position):
"""Set text actor position.

Expand All @@ -1091,22 +1125,11 @@
self.background.position = position

def _get_size(self):
if self.have_bg:
return self.background.size

if not self.actor.GetTextScaleMode():
if self.scene is not None:
size = np.zeros(2)
self.actor.GetSize(self.scene, size)
return size
else:
warn(
'TextBlock2D must be added to the scene before '
'querying its size while TextScaleMode is set to None.',
RuntimeWarning,
)

return self.actor.GetPosition2()
bb_size = (self.boundingbox[2]-self.boundingbox[0],
self.boundingbox[3]-self.boundingbox[1])
if self.dynamic_bbox or self.auto_font_scale or sum(bb_size):
return bb_size
return self.cal_size_from_message()


class Button2D(UI):
Expand Down
5 changes: 3 additions & 2 deletions fury/ui/elements.py
Expand Up @@ -131,7 +131,7 @@ def _setup(self):

Create the TextBlock2D component used for the textbox.
"""
self.text = TextBlock2D()
self.text = TextBlock2D(dynamic_bbox=True)

# Add default events listener for this UI component.
self.text.on_left_mouse_button_pressed = self.left_button_press
Expand Down Expand Up @@ -1445,7 +1445,8 @@ def _setup(self):
self.handle.color = self.default_color

# Slider Text
self.text = TextBlock2D(justification='center', vertical_justification='middle')
self.text = TextBlock2D(justification='center',
vertical_justification='middle')

# Add default events listener for this UI component.
self.track.on_left_mouse_button_pressed = self.track_click_callback
Expand Down
13 changes: 4 additions & 9 deletions fury/ui/helpers.py
Expand Up @@ -51,7 +51,6 @@ def wrap_overflow(textblock, wrap_width, side='right'):
"""
original_str = textblock.message
str_copy = textblock.message
prev_bg = textblock.have_bg
wrap_idxs = []

wrap_idx = check_overflow(textblock, wrap_width, '', side)
Expand All @@ -72,7 +71,6 @@ def wrap_overflow(textblock, wrap_width, side='right'):
original_str = original_str[:idx] + '\n' + original_str[idx:]

textblock.message = original_str
textblock.have_bg = prev_bg
return textblock.message


Expand All @@ -99,26 +97,23 @@ def check_overflow(textblock, width, overflow_postfix='', side='right'):
start_ptr = 0
mid_ptr = 0
end_ptr = len(original_str)
prev_bg = textblock.have_bg
textblock.have_bg = False

if side == 'left':
original_str = original_str[::-1]

if textblock.size[0] <= width:
textblock.have_bg = prev_bg
if textblock.cal_size_from_message()[0] <= width:
return 0

while start_ptr < end_ptr:
mid_ptr = (start_ptr + end_ptr) // 2
textblock.message = original_str[:mid_ptr] + overflow_postfix

if textblock.size[0] < width:
if textblock.cal_size_from_message()[0] < width:
start_ptr = mid_ptr
elif textblock.size[0] > width:
elif textblock.cal_size_from_message()[0] > width:
end_ptr = mid_ptr

if mid_ptr == (start_ptr + end_ptr) // 2 or textblock.size[0] == width:
if mid_ptr == (start_ptr + end_ptr) // 2 or textblock.cal_size_from_message()[0] == width:
if side == 'left':
textblock.message = textblock.message[::-1]
return mid_ptr
Expand Down
6 changes: 3 additions & 3 deletions fury/ui/tests/test_containers.py
Expand Up @@ -285,9 +285,9 @@ def test_ui_tab_ui(interactive=False):
npt.assert_equal(tab_ui.tabs[1].title_color, (.0, 1., .0))
npt.assert_equal(tab_ui.tabs[2].title_color, (.0, .0, 1.))

npt.assert_equal(tab_ui.tabs[0].title_font_size, 12)
npt.assert_equal(tab_ui.tabs[1].title_font_size, 12)
npt.assert_equal(tab_ui.tabs[2].title_font_size, 12)
npt.assert_equal(tab_ui.tabs[0].title_font_size, 18)
npt.assert_equal(tab_ui.tabs[1].title_font_size, 18)
npt.assert_equal(tab_ui.tabs[2].title_font_size, 18)

tab_ui.tabs[0].title_font_size = 10
tab_ui.tabs[1].title_font_size = 20
Expand Down