Skip to content

Commit

Permalink
Improvements to help with switching areas and a few bug fixes
Browse files Browse the repository at this point in the history
  • Loading branch information
mmmrqs committed Sep 27, 2022
1 parent 49ca3fb commit d9c9551
Show file tree
Hide file tree
Showing 7 changed files with 233 additions and 88 deletions.
5 changes: 4 additions & 1 deletion __init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
bl_info = {"name": "BL UI Widgets",
"description": "UI Widgets to draw in the 3D view",
"author": "Marcelo M. Marques (fork of Jayanam's original project)",
"version": (1, 0, 2),
"version": (1, 0, 3),
"blender": (2, 80, 75),
"location": "View3D > side panel ([N]), [BL_UI_Widget] tab",
"support": "COMMUNITY",
Expand All @@ -35,6 +35,9 @@
# Note: Because the way Blender's Preferences window displays the Addon version number,
# I am forced to keep this file in sync with the greatest version number of all modules.

# v1.0.3 (09.25.2021) - by Marcelo M. Marques
# Chang: updated version to keep this file in sync

# v1.0.2 (10.31.2021) - by Marcelo M. Marques
# Chang: updated version with improvements and some clean up

Expand Down
26 changes: 21 additions & 5 deletions bl_ui_drag_panel.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
bl_info = {"name": "BL UI Widgets",
"description": "UI Widgets to draw in the 3D view",
"author": "Marcelo M. Marques (fork of Jayanam's original project)",
"version": (1, 0, 1),
"version": (1, 0, 2),
"blender": (2, 80, 75),
"location": "View3D > viewport area",
"support": "COMMUNITY",
Expand All @@ -32,6 +32,11 @@

# --- ### Change log

# v1.0.2 (09.25.2022) - by Marcelo M. Marques
# Added: 'quadview' property to indicate whether panel is opened in the QuadView mode or not
# Chang: Logic to save panel screen position only when not in QuadView mode. In some future release this may get improved
# to save the position in both cases, but to distinct sets of variables in the session's saved data dictionary

# v1.0.1 (09.20.2021) - by Marcelo M. Marques
# Chang: just some pep8 code formatting

Expand Down Expand Up @@ -105,6 +110,7 @@ def __init__(self, x, y, width, height):
self._has_shadow = False # Indicates whether a shadow must be drawn around the panel

self._anchored = False # Indicates whether panel can be dragged around the viewport or not
self._quadview = False # Indicates whether panel is opened in the QuadView mode or not

self.__drag_offset_x = 0
self.__drag_offset_y = 0
Expand All @@ -118,6 +124,14 @@ def anchored(self):
def anchored(self, value):
self._anchored = value

@property
def quadview(self):
return self._quadview

@quadview.setter
def quadview(self, value):
self._quadview = value

def add_widget(self, widget):
self.widgets.append(widget)

Expand All @@ -137,7 +151,9 @@ def child_widget_focused(self, x, y):
return False

def save_panel_coords(self, x, y):
# Update the new coord values in the session's saved data dictionary.
# Update the new coord values in the session's saved data dictionary, only when not in QuadView mode
if self.quadview:
return None
# Note: Because of the scaling logic it was necessary to make this weird correction math below
new_x = self.over_scale(x)
new_y = self.over_scale(y)
Expand Down Expand Up @@ -172,13 +188,13 @@ def set_location(self, x, y):

# Overrides base class function
def mouse_down(self, event, x, y):
if self.child_widget_focused(x, y):
# Means the focus is on some sub-widget (e.g.: a button)
return False
if self.anchored:
# Means the panel is not draggable
return False
if self.is_in_rect(x, y):
if self.child_widget_focused(x, y):
# Means the focus is on some sub-widget (e.g.: a button)
return False
# When panel is disabled, just ignore the click
if self._is_enabled:
height = self.get_area_height()
Expand Down
108 changes: 62 additions & 46 deletions bl_ui_draw_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
bl_info = {"name": "BL UI Widgets",
"description": "UI Widgets to draw in the 3D view",
"author": "Marcelo M. Marques (fork of Jayanam's original project)",
"version": (1, 0, 2),
"version": (1, 0, 3),
"blender": (2, 80, 75),
"location": "View3D > viewport area",
"support": "COMMUNITY",
Expand All @@ -32,6 +32,10 @@

# --- ### Change log

# v1.0.3 (09.25.2021) - by Marcelo M. Marques
# Added: Many improvements to help with identifying when the panel must be automatically terminated either due to a change
# in region or for any other issues. These changes may help with making the code more stable and reliable.

# v1.0.2 (10.31.2021) - by Marcelo M. Marques
# Added: 'region_pointer' class level property to indicate the region in which the drag_panel operator instance has been invoked().
# Added: 'valid_modes' property to indicate the 'bpy.context.mode' valid values for displaying the panel.
Expand Down Expand Up @@ -79,8 +83,8 @@ class BL_UI_OT_draw_operator(Operator):
def __init__(self):
self.widgets = []
self.valid_modes = []
# self.__draw_handle = None # <-- Was like this before implementing the 'lost handler detection logic'
# self.__draw_events = None # (ditto)
# self.__draw_handle = None # <-- Was like this before I had implemented the 'lost handler detection logic'
# self.__draw_events = None # <--(ditto)
self.__finished = False
self.__informed = False

Expand Down Expand Up @@ -120,9 +124,9 @@ def invoke(self, context, event):
# Avoid "internal error: modal gizmo-map handler has invalid area" terminal messages, after maximizing the viewport,
# by switching the workspace back and forth. Not pretty, but at least it avoids the terminal output getting spammed.
current = context.workspace
other = [ws for ws in bpy.data.workspaces if ws != current]
if other:
bpy.context.window.workspace = other[0]
others = [ws for ws in bpy.data.workspaces if ws != current]
if others:
bpy.context.window.workspace = others[0]
bpy.context.window.workspace = current
# -----------------------------------------------------------------
BL_UI_OT_draw_operator.region_pointer = context.region.as_pointer()
Expand Down Expand Up @@ -165,7 +169,7 @@ def modal(self, context, event):
if self.handle_widget_events(event, area, region):
return {'RUNNING_MODAL'}

# Not using this escape option, but left it here for documentation purpose
# Not using any escape option, but left it here for documentation purposes
# if event.type in {"ESC"}:
# self.finish()

Expand All @@ -178,18 +182,24 @@ def valid_scenario(self, context, event):
self.finish()
valid = False
elif not (area and region):
if self.terminate_execution(area, region, event):
self.finish()
# Return but do not finish
valid = False
elif self.terminate_execution(area, region):
elif self.terminate_execution(area, region, event):
area.tag_redraw()
self.finish()
valid = False
elif event.type != 'TIMER':
# Check whether it is drawing on the same region where the panel was initially opened
mouse_region = get_quadview_index(context, event.mouse_x, event.mouse_y)[0]
if mouse_region is None or mouse_region.as_pointer() != self.get_region_pointer():
# Not the same region, so skip handling events at this time
mouse_region, abend = get_region(context, event.mouse_x, event.mouse_y)
if abend:
self.finish()
valid = False
else:
if mouse_region is None or mouse_region.as_pointer() != self.get_region_pointer():
# Not the same region, so skip handling events at this time, but do not finish
valid = False
return (valid, area, region)

def handle_widget_events(self, event, area, region):
Expand All @@ -216,7 +226,7 @@ def suppress_rendering(self, area, region):
# This might be overriden by one same named function in the derived (child) class
return False

def terminate_execution(self, area, region):
def terminate_execution(self, area, region, event):
# This might be overriden by one same named function in the derived (child) class
return False

Expand All @@ -231,27 +241,31 @@ def finish(self):
self.unregister_handlers(bpy.context)
self.on_finish(bpy.context)

def cancel(self, context):
# Called when Blender cancels the modal operator
self.finish()

# Draw handler to paint onto the screen
def draw_callback_px(self, op, context):
# Check whether handles are still valid
if not BL_UI_OT_draw_operator.valid_handler():
# -- personalized criteria for the Remote Control panel addon --
# This is a temporary workaround till I figure out how to signal to
# the N-panel coding that the remote control panel has been finished.
bpy.context.scene.var.RemoVisible = False
bpy.context.scene.var.btnRemoText = "Open Remote Control"
# -- end of the personalized criteria for the given addon --
try:
# -- personalized criteria for the Remote Control panel addon --
# This is a temporary workaround till I figure out how to signal to
# the N-panel coding that the remote control panel has been finished.
bpy.context.scene.var.RemoVisible = False
bpy.context.scene.var.btnRemoText = "Open Remote Control"
# -- end of the personalized criteria for the given addon --
except:
pass
return

# Check whether it is drawing on the same region where the panel was initially opened
for region in context.area.regions:
if region.type == 'WINDOW':
if context.region == region:
if region.as_pointer() != self.get_region_pointer():
# Not the same region, so skip drawing there
return
break

for region in [region for region in context.area.regions if region.type == 'WINDOW']:
if context.region == region:
if region.as_pointer() != self.get_region_pointer():
# Not the same region, so skip drawing there
return
break
# This is to detect when user moved into an undesired 'bpy.context.mode'
# and it will check also the programmer's defined suppress_rendering function
if valid_display_mode(self.valid_modes, self.suppress_rendering):
Expand All @@ -261,22 +275,26 @@ def draw_callback_px(self, op, context):

# --- ### Helper functions

def get_quadview_index(context, x, y):
for area in context.screen.areas:
if area.type != 'VIEW_3D':
continue
is_quadview = (len(area.spaces.active.region_quadviews) == 0)
i = -1
for region in area.regions:
if region.type == 'WINDOW':
i += 1
def get_region(context, x, y):
abend = False
try:
for area in [area for area in context.screen.areas if area.type == 'VIEW_3D']:
for region in [region for region in area.regions if region.type == 'WINDOW']:
if (x >= region.x and
y >= region.y and
x < region.width + region.x and
y < region.height + region.y):
return (region, area.spaces.active, None if is_quadview else i)
return (None, None, None)

return (region, abend)
except Exception as e:
if __package__.find(".") != -1:
package = __package__[0:__package__.find(".")]
else:
package = __package__
print("**WARNING** " + package + " addon issue:")
print(" +--> unexpected result in 'get_region' function of bl_ui_draw_op.py module!")
print(" " + e)
abend = True
return (None, abend)

def get_3d_area_and_region(prefs=None):
abend = False
Expand All @@ -303,14 +321,12 @@ def get_3d_area_and_region(prefs=None):
# if region.type == 'WINDOW':
# if region.as_pointer() == BL_UI_OT_draw_operator.region_pointer:
# return (area, region, abend)

#
for screen in bpy.data.screens:
for area in screen.areas:
if area.type == 'VIEW_3D':
for region in area.regions:
if region.type == 'WINDOW':
if region.as_pointer() == BL_UI_OT_draw_operator.region_pointer:
return (area, region, abend)
for area in [area for area in screen.areas if area.type == 'VIEW_3D']:
for region in [region for region in area.regions if region.type == 'WINDOW']:
if region.as_pointer() == BL_UI_OT_draw_operator.region_pointer:
return (area, region, abend)
except Exception as e:
if __package__.find(".") != -1:
package = __package__[0:__package__.find(".")]
Expand Down
34 changes: 28 additions & 6 deletions bl_ui_slider.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
bl_info = {"name": "BL UI Widgets",
"description": "UI Widgets to draw in the 3D view",
"author": "Marcelo M. Marques (fork of Jayanam's original project)",
"version": (1, 0, 2),
"version": (1, 0, 3),
"blender": (2, 80, 75),
"location": "View3D > viewport area",
"support": "COMMUNITY",
Expand All @@ -32,15 +32,21 @@

# --- ### Change log

# v1.0.3 (09.25.2022) - by Marcelo M. Marques
# Added: 'is_editable' property to indicate if, for the 'NUMBER_SLIDE' style, the user is allowed to change the value by text editing.
# Chang: 'slider_mouse_up_func' function to prevent text editing per property documented above, and gave more flexibility to its logic by
# adding calls to both the 'value_changed_func' and 'update_self_value' functions so user has a final chance to override the changes.
# Fixed: Small issue in the 'calc_slider_bar' function that in some cases would return a float value when an integer was expected.

# v1.0.2 (10.31.2021) - by Marcelo M. Marques
# Added: 'valid_modes' parm in the 'init' function and a call to 'super().init_mode' so we get everything correctly initialized.
# Chang: improved reliability on 'mouse_exit' and 'button_mouse_down' overridable functions by conditioning the returned value
# Chang: Improved reliability on 'mouse_exit' and 'button_mouse_down' overridable functions by conditioning the returned value.

# v1.0.1 (09.20.2021) - by Marcelo M. Marques
# Chang: just some pep8 code formatting
# Chang: Just some pep8 code formatting.

# v1.0.0 (09.01.2021) - by Marcelo M. Marques
# Added: Logic to scale the slider according to both Blender's ui scale configuration and this addon 'preferences' setup
# Added: Logic to scale the slider according to both Blender's ui scale configuration and this addon 'preferences' setup.
# Added: 'outline_color' property to allow different color on the slider outline (value is standard color tuple).
# Added: 'roundness' property to allow the slider to be painted with rounded corners,
# same as that property available in Blender's user themes and it works together with 'rounded_corners' below.
Expand Down Expand Up @@ -101,6 +107,7 @@ def __init__(self, x, y, width, height):
self._step = 1 # Step size to increase/decrease the displayed value (in precision units)
self._unit = "" # Unit indicator for the displayed value
self._max_input_chars = 20 # Maximum number of characters to be input by the textbox
self._is_editable = True # Indicates whether the value can be changed by text editing

self._text_margin = 16 # Slider text left/right margins (when 'NOT' in Textbox edit mode)
self._text_size = None # Slider text size
Expand Down Expand Up @@ -354,6 +361,14 @@ def max_input_chars(self):
def max_input_chars(self, value):
self._max_input_chars = value

@property
def is_editable(self):
return self._is_editable

@is_editable.setter
def is_editable(self, value):
self._is_editable = value

@property
def text(self):
return self._text
Expand Down Expand Up @@ -463,7 +478,7 @@ def textbox_str_value(self, value):
str_value = str_value[:len(str_value) - 2] if str_value[-2:] == ".0" else str_value
return str_value

def update_self_value(self, value, mode):
def update_self_value(self, value, mode='DEFAULT'):
update_value = value
if self._min_value is not None:
update_value = self._min_value if update_value < self._min_value else update_value
Expand Down Expand Up @@ -496,7 +511,7 @@ def calc_slider_bar(self, value):
percentage = 0 if percentage < 0 else percentage
percentage = 1 if percentage > 1 else percentage
slider_bar_width = int(round(self.slider.width * percentage))
slider_bar_pos_x = self.over_scale(self.slider.x_screen + slider_bar_width - 1)
slider_bar_pos_x = int(self.over_scale(self.slider.x_screen + slider_bar_width - 1))
return [slider_bar_width, slider_bar_pos_x]

# Overrides base class function
Expand Down Expand Up @@ -906,6 +921,13 @@ def slider_mouse_down_func(self, widget, event, x, y):
def slider_mouse_up_func(self, widget, event, x, y):
self.__is_dragging = False
if self.__mouse_moved:
if self._style == 'NUMBER_SLIDE':
# Calls user function to give it a chance to override the changes
self.value_changed_func(self, self._value)
self.update_self_value(self._value)
# Indicates that sliding mode has finished
self.set_exclusive_mode(None)
elif not self._is_editable:
# Indicates that sliding mode has finished
self.set_exclusive_mode(None)
else:
Expand Down
Loading

0 comments on commit d9c9551

Please sign in to comment.