diff options
author | Vilém Duha <vilda.novak@gmail.com> | 2021-01-17 15:35:23 +0300 |
---|---|---|
committer | Vilém Duha <vilda.novak@gmail.com> | 2021-01-17 15:35:23 +0300 |
commit | e237c47811583d92c2ad07f00591cf4499fa0ae4 (patch) | |
tree | e644088d8bcb03cad9e8c6ad4f4579823ff2b4ca /blenderkit/asset_bar_op.py | |
parent | ebdf1861dc568a9b4b92b29a9b8f7117ad1eec05 (diff) |
BlenderKit: fixing asset bar code
This is now only included under experimental features. It includes a stripped down version of bl_ui_widngets library by Jayanam. Basically the asset bar code got split and now both asset bar codes are next to each other.
Diffstat (limited to 'blenderkit/asset_bar_op.py')
-rw-r--r-- | blenderkit/asset_bar_op.py | 591 |
1 files changed, 591 insertions, 0 deletions
diff --git a/blenderkit/asset_bar_op.py b/blenderkit/asset_bar_op.py new file mode 100644 index 00000000..612f5885 --- /dev/null +++ b/blenderkit/asset_bar_op.py @@ -0,0 +1,591 @@ +import bpy + +from bpy.types import Operator + +from blenderkit.bl_ui_widgets.bl_ui_label import * +from blenderkit.bl_ui_widgets.bl_ui_button import * +# from blenderkit.bl_ui_widgets.bl_ui_checkbox import * +# from blenderkit.bl_ui_widgets.bl_ui_slider import * +# from blenderkit.bl_ui_widgets.bl_ui_up_down import * +from blenderkit.bl_ui_widgets.bl_ui_drag_panel import * +from blenderkit.bl_ui_widgets.bl_ui_draw_op import * +# from blenderkit.bl_ui_widgets.bl_ui_textbox import * +import random +import math + +import blenderkit +from blenderkit import ui, paths, utils, search + +from bpy.props import ( + IntProperty, + BoolProperty, + StringProperty +) + + +def draw_callback_tooltip(self, context): + if self.draw_tooltip: + s = bpy.context.scene + sr = s.get('search results') + r = sr[self.active_index] + ui.draw_tooltip_with_author(r, 0, 500) + +def get_area_height(self): + if type(self.context)!= dict: + self.context = self.context.copy() + # print(self.context) + if self.context.get('area') is not None: + return self.context['area'].height + # else: + # maxw, maxa, region = utils.get_largest_area() + # if maxa: + # self.context['area'] = maxa + # self.context['window'] = maxw + # self.context['region'] = region + # self.update(self.x,self.y) + # + # return self.context['area'].height + # print('no area found') + return 100 + +BL_UI_Widget.get_area_height = get_area_height + + +def asset_bar_modal(self, context, event): + if self._finished: + return {'FINISHED'} + + if context.area: + context.area.tag_redraw() + else: + self.finish() + return {'FINISHED'} + + if self.handle_widget_events(event): + return {'RUNNING_MODAL'} + + if event.type in {"ESC"}: + self.finish() + + if event.type == 'WHEELUPMOUSE': + self.scroll_offset -= 5 + self.scroll_update() + return {'RUNNING_MODAL'} + elif event.type == 'WHEELDOWNMOUSE': + self.scroll_offset += 5 + self.scroll_update() + return {'RUNNING_MODAL'} + + + return {"PASS_THROUGH"} + +def asset_bar_invoke(self, context, event): + + if not self.on_invoke(context, event): + return {"CANCELLED"} + + args = (self, context) + + self.register_handlers(args, context) + + context.window_manager.modal_handler_add(self) + return {"RUNNING_MODAL"} + +BL_UI_OT_draw_operator.modal = asset_bar_modal +BL_UI_OT_draw_operator.invoke = asset_bar_invoke + + +def set_mouse_down_right(self, mouse_down_right_func): + self.mouse_down_right_func = mouse_down_right_func + + +def mouse_down_right(self, x, y): + if self.is_in_rect(x, y): + self.__state = 1 + try: + self.mouse_down_right_func(self) + except Exception as e: + print(e) + + return True + + return False + +# def handle_event(self, event): +# x = event.mouse_region_x +# y = event.mouse_region_y +# +# if (event.type == 'LEFTMOUSE'): +# if (event.value == 'PRESS'): +# self._mouse_down = True +# return self.mouse_down(x, y) +# else: +# self._mouse_down = False +# self.mouse_up(x, y) +# +# elif (event.type == 'RIGHTMOUSE'): +# if (event.value == 'PRESS'): +# self._mouse_down_right = True +# return self.mouse_down_right(x, y) +# else: +# self._mouse_down_right = False +# self.mouse_up(x, y) +# +# elif (event.type == 'MOUSEMOVE'): +# self.mouse_move(x, y) +# +# inrect = self.is_in_rect(x, y) +# +# # we enter the rect +# if not self.__inrect and inrect: +# self.__inrect = True +# self.mouse_enter(event, x, y) +# +# # we are leaving the rect +# elif self.__inrect and not inrect: +# self.__inrect = False +# self.mouse_exit(event, x, y) +# +# return False +# +# elif event.value == 'PRESS' and (event.ascii != '' or event.type in self.get_input_keys()): +# return self.text_input(event) +# +# return False + +BL_UI_Button.mouse_down_right = mouse_down_right +BL_UI_Button.set_mouse_down_right = set_mouse_down_right +# BL_UI_Button.handle_event = handle_event + + + + +class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): + bl_idname = "view3d.blenderkit_asset_bar_widget" + bl_label = "BlenderKit asset bar refresh" + bl_description = "BlenderKit asset bar refresh" + bl_options = {'REGISTER'} + + do_search: BoolProperty(name="Run Search", description='', default=True, options={'SKIP_SAVE'}) + keep_running: BoolProperty(name="Keep Running", description='', default=True, options={'SKIP_SAVE'}) + free_only: BoolProperty(name="Free first", description='', default=False, options={'SKIP_SAVE'}) + + category: StringProperty( + name="Category", + description="search only subtree of this category", + default="", options={'SKIP_SAVE'}) + + tooltip: bpy.props.StringProperty(default='runs search and displays the asset bar at the same time') + + @classmethod + def description(cls, context, properties): + return properties.tooltip + + def new_text(self, text, x, y, width=100, height=15, text_size=None): + label = BL_UI_Label(x, y, width, height) + label.text = text + if text_size is None: + text_size = 14 + label.text_size = text_size + label.text_color = self.text_color + return label + + def init_tooltip(self): + self.tooltip_widgets = [] + tooltip_size = 500 + total_size = tooltip_size + 2 * self.assetbar_margin + self.tooltip_panel = BL_UI_Drag_Panel(0, 0, total_size, total_size) + self.tooltip_panel.bg_color = (0.0, 0.0, 0.0, 0.5) + self.tooltip_panel.visible = False + + tooltip_image = BL_UI_Button(self.assetbar_margin, self.assetbar_margin, 1, 1) + tooltip_image.text = "" + img_path = paths.get_addon_thumbnail_path('thumbnail_notready.jpg') + tooltip_image.set_image(img_path) + tooltip_image.set_image_size((tooltip_size, tooltip_size)) + tooltip_image.set_image_position((0, 0)) + self.tooltip_image = tooltip_image + self.tooltip_widgets.append(tooltip_image) + + bottom_panel_fraction = 0.1 + labels_start = total_size * (1 - bottom_panel_fraction) - self.margin + + dark_panel = BL_UI_Widget(0, labels_start, total_size, total_size * bottom_panel_fraction) + dark_panel.bg_color = (0.0, 0.0, 0.0, 0.7) + self.tooltip_widgets.append(dark_panel) + + name_label = self.new_text('', self.assetbar_margin*2, labels_start, text_size=16) + self.asset_name = name_label + self.tooltip_widgets.append(name_label) + offset_y = 16 + self.margin + label = self.new_text('Left click or drag to append/link. Right click for more options.', self.assetbar_margin*2, labels_start + offset_y, + text_size=14) + self.tooltip_widgets.append(label) + + + self.hide_tooltip() + + def hide_tooltip(self): + self.tooltip_panel.visible = False + for w in self.tooltip_widgets: + w.visible = False + + def show_tooltip(self): + self.tooltip_panel.visible = True + for w in self.tooltip_widgets: + w.visible = True + + def update_ui_size(self, context): + + if bpy.app.background or not context.area: + return + + region = context.region + area = context.area + + ui_props = bpy.context.scene.blenderkitUI + user_preferences = bpy.context.preferences.addons['blenderkit'].preferences + ui_scale = bpy.context.preferences.view.ui_scale + + self.margin = ui_props.bl_rna.properties['margin'].default * ui_scale + self.margin = 3 + self.assetbar_margin = self.margin + + self.thumb_size = user_preferences.thumb_size * ui_scale + self.button_size = 2 * self.margin + self.thumb_size + + reg_multiplier = 1 + if not bpy.context.preferences.system.use_region_overlap: + reg_multiplier = 0 + + for r in area.regions: + if r.type == 'TOOLS': + self.bar_x = r.width * reg_multiplier + self.margin + ui_props.bar_x_offset * ui_scale + elif r.type == 'UI': + self.bar_end = r.width * reg_multiplier + 100 * ui_scale + + self.bar_width = region.width - ui_props.bar_x - ui_props.bar_end + + self.wcount = math.floor( + (self.bar_width) / (self.button_size)) + + search_results = bpy.context.scene.get('search results') + if search_results is not None and self.wcount > 0: + self.hcount = min(user_preferences.max_assetbar_rows, math.ceil(len(search_results) / self.wcount)) + else: + self.hcount = 1 + + self.bar_height = (self.button_size) * self.hcount + 2 * self.assetbar_margin + # self.bar_y = region.height - ui_props.bar_y_offset * ui_scale + self.bar_y = ui_props.bar_y_offset * ui_scale + if ui_props.down_up == 'UPLOAD': + self.reports_y = self.bar_y - 600 + self.reports_x = self.bar_x + else: + self.reports_y = self.bar_y - self.bar_height - 100 + self.reports_x = self.bar_x + + def __init__(self): + super().__init__() + + self.update_ui_size(bpy.context) + + ui_props = bpy.context.scene.blenderkitUI + + # todo move all this to update UI size + + self.draw_tooltip = False + self.scroll_offset = 0 + + self.text_color = (0.9, 0.9, 0.9, 1.0) + button_bg_color = (0.2, 0.2, 0.2, .1) + button_hover_color = (0.8, 0.8, 0.8, .2) + + self.init_tooltip() + + self.buttons = [] + self.asset_buttons = [] + self.validation_icons = [] + self.widgets_panel = [] + + self.panel = BL_UI_Drag_Panel(0, 0, self.bar_width, self.bar_height) + self.panel.bg_color = (0.0, 0.0, 0.0, 0.5) + + sr = bpy.context.scene['search results'] + + for a in range(0, self.wcount): + for b in range(0, self.hcount): + asset_x = self.assetbar_margin + a * (self.button_size) + asset_y = self.assetbar_margin + b * (self.button_size) + new_button = BL_UI_Button(asset_x, asset_y, self.button_size, self.button_size) + + asset_idx = a + b * self.wcount + self.scroll_offset + # asset_data = sr[asset_idx] + # iname = blenderkit.utils.previmg_name(asset_idx) + # img = bpy.data.images.get(iname) + + new_button.bg_color = button_bg_color + new_button.hover_bg_color = button_hover_color + new_button.text = "" # asset_data['name'] + # if img: + # new_button.set_image(img.filepath) + + new_button.set_image_size((self.thumb_size, self.thumb_size)) + new_button.set_image_position((self.margin, self.margin)) + new_button.button_index = asset_idx + new_button.search_index = asset_idx + new_button.set_mouse_down(self.drag_drop_asset) + new_button.set_mouse_down_right(self.asset_menu) + new_button.set_mouse_enter(self.enter_button) + new_button.set_mouse_exit(self.exit_button) + new_button.text_input = self.handle_key_input + self.asset_buttons.append(new_button) + # add validation icon to button + icon_size = 24 + validation_icon = BL_UI_Button(asset_x + self.button_size - icon_size - self.margin, + asset_y + self.button_size - icon_size - self.margin, 0, 0) + + # v_icon = ui.verification_icons[asset_data.get('verificationStatus', 'validated')] + # if v_icon is not None: + # img_fp = paths.get_addon_thumbnail_path(v_icon) + # validation_icon.set_image(img_fp) + validation_icon.text = '' + validation_icon.set_image_size((icon_size, icon_size)) + validation_icon.set_image_position((0, 0)) + self.validation_icons.append(validation_icon) + new_button.validation_icon = validation_icon + + other_button_size = 30 + + self.button_close = BL_UI_Button(self.bar_width - other_button_size, -0, other_button_size, 15) + self.button_close.bg_color = button_bg_color + self.button_close.hover_bg_color = button_hover_color + self.button_close.text = "x" + self.button_close.set_mouse_down(self.cancel_press) + + self.widgets_panel.append(self.button_close) + scroll_width = 30 + self.button_scroll_down = BL_UI_Button(-scroll_width, 0, scroll_width, self.bar_height) + self.button_scroll_down.bg_color = button_bg_color + self.button_scroll_down.hover_bg_color = button_hover_color + self.button_scroll_down.text = "" + self.button_scroll_down.set_image(paths.get_addon_thumbnail_path('arrow_left.png')) + self.button_scroll_down.set_image_size((scroll_width, self.button_size)) + self.button_scroll_down.set_image_position((0, int((self.bar_height - self.button_size) / 2))) + + self.button_scroll_down.set_mouse_down(self.scroll_down) + + self.widgets_panel.append(self.button_scroll_down) + + self.button_scroll_up = BL_UI_Button(self.bar_width, 0, scroll_width, self.bar_height) + self.button_scroll_up.bg_color = button_bg_color + self.button_scroll_up.hover_bg_color = button_hover_color + self.button_scroll_up.text = "" + self.button_scroll_up.set_image(paths.get_addon_thumbnail_path('arrow_right.png')) + self.button_scroll_up.set_image_size((scroll_width, self.button_size)) + self.button_scroll_up.set_image_position((0, int((self.bar_height - self.button_size) / 2))) + + self.button_scroll_up.set_mouse_down(self.scroll_up) + + self.widgets_panel.append(self.button_scroll_up) + + self.update_images() + + def on_invoke(self, context, event): + + + if self.do_search: + #TODO: move the search behaviour to separate operator, since asset bar can be already woken up from a timer. + + # we erase search keywords for cateogry search now, since these combinations usually return nothing now. + # when the db gets bigger, this can be deleted. + if self.category != '': + sprops = utils.get_search_props() + sprops.search_keywords = '' + search.search(category=self.category) + + ui_props = context.scene.blenderkitUI + if ui_props.assetbar_on: + #TODO solve this otehrwise to enable more asset bars? + + # we don't want to run the assetbar many times, that's why it has a switch on/off behaviour, + # unless being called with 'keep_running' prop. + if not self.keep_running: + # this sends message to the originally running operator, so it quits, and then it ends this one too. + # If it initiated a search, the search will finish in a thread. The switch off procedure is run + # by the 'original' operator, since if we get here, it means + # same operator is already running. + ui_props.turn_off = True + # if there was an error, we need to turn off these props so we can restart after 2 clicks + ui_props.assetbar_on = False + + else: + pass + return False + + ui_props.assetbar_on = True + + self.active_index = -1 + + widgets_panel = self.widgets_panel + widgets_panel.extend(self.buttons) + widgets_panel.extend(self.asset_buttons) + widgets_panel.extend(self.validation_icons) + + widgets = [self.panel] + + widgets += widgets_panel + widgets.append(self.tooltip_panel) + widgets += self.tooltip_widgets + + self.init_widgets(context, widgets) + + self.panel.add_widgets(widgets_panel) + self.tooltip_panel.add_widgets(self.tooltip_widgets) + + # Open the panel at the mouse location + # self.panel.set_location(bpy.context.area.width - event.mouse_x, + # bpy.context.area.height - event.mouse_y + 20) + self.panel.set_location(self.bar_x, + self.bar_y) + + self.context = context + args = (self, context) + + # self._handle_2d_tooltip = bpy.types.SpaceView3D.draw_handler_add(draw_callback_tooltip, args, 'WINDOW', 'POST_PIXEL') + return True + + def on_finish(self, context): + # redraw all areas, since otherwise it stays to hang for some more time. + # bpy.types.SpaceView3D.draw_handler_remove(self._handle_2d_tooltip, 'WINDOW') + + scene = bpy.context.scene + ui_props = scene.blenderkitUI + ui_props.assetbar_on = False + + wm = bpy.data.window_managers[0] + + for w in wm.windows: + for a in w.screen.areas: + a.tag_redraw() + + self._finished = True + + # handlers + + def enter_button(self, widget): + self.show_tooltip() + + if self.active_index != widget.search_index: + scene = bpy.context.scene + sr = scene['search results'] + asset_data = sr[widget.search_index + self.scroll_offset] + + self.active_index = widget.search_index + self.draw_tooltip = True + self.tooltip = asset_data['tooltip'] + ui_props = scene.blenderkitUI + ui_props.active_index = widget.search_index +self.scroll_offset + + img = ui.get_large_thumbnail_image(asset_data) + if img: + self.tooltip_image.set_image(img.filepath) + self.asset_name.text = asset_data['name'] + self.tooltip_panel.update(widget.x_screen + widget.width, widget.y_screen + widget.height) + self.tooltip_panel.layout_widgets() + + def exit_button(self, widget): + # this condition checks if there wasn't another button already entered, which can happen with small button gaps + if self.active_index == widget.search_index: + scene = bpy.context.scene + ui_props = scene.blenderkitUI + ui_props.draw_tooltip = False + self.draw_tooltip = False + self.hide_tooltip() + + def drag_drop_asset(self, widget): + bpy.ops.view3d.asset_drag_drop('INVOKE_DEFAULT', asset_search_index=widget.search_index + self.scroll_offset) + + def cancel_press(self, widget): + self.finish() + + def asset_menu(self, widget): + bpy.ops.wm.blenderkit_asset_popup('INVOKE_DEFAULT') + # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu') + + def search_more(self): + sro = bpy.context.scene.get('search results orig') + if sro is not None and sro.get('next') is not None: + blenderkit.search.search(get_next=True) + def update_images(self): + sr = bpy.context.scene['search results'] + + for asset_button in self.asset_buttons: + asset_button.asset_index = asset_button.button_index + self.scroll_offset + if asset_button.asset_index < len(sr): + asset_button.visible = True + + asset_data = sr[asset_button.asset_index] + + iname = blenderkit.utils.previmg_name(asset_button.asset_index) + # show indices for debug purposes + # asset_button.text = str(asset_button.asset_index) + img = bpy.data.images.get(iname) + if not img: + img_filepath = paths.get_addon_thumbnail_path('thumbnail_notready.jpg') + else: + img_filepath = img.filepath + asset_button.set_image(img_filepath) + v_icon = ui.verification_icons[asset_data.get('verificationStatus', 'validated')] + if v_icon is not None: + img_fp = paths.get_addon_thumbnail_path(v_icon) + asset_button.validation_icon.set_image(img_fp) + asset_button.validation_icon.visible = True + else: + asset_button.validation_icon.visible = False + else: + asset_button.visible = False + asset_button.validation_icon.visible = False + + def scroll_update(self): + sr = bpy.context.scene['search results'] + self.scroll_offset = min(self.scroll_offset, len(sr) - (self.wcount * self.hcount)) + self.scroll_offset = max(self.scroll_offset, 0) + self.update_images() + if len(sr) - self.scroll_offset < (self.wcount * self.hcount) + 10: + self.search_more() + + def search_by_author(self, asset_index): + sr = bpy.context.scene['search results'] + asset_data = sr[asset_index] + a = asset_data['author']['id'] + if a is not None: + sprops = utils.get_search_props() + sprops.search_keywords = '' + sprops.search_verification_status = 'ALL' + utils.p('author:', a) + search.search(author_id=a) + return True + + def handle_key_input(self, event): + if event.type == 'A': + self.search_by_author(self.active_index + self.scroll_offset) + return False + + def scroll_up(self, widget): + sr = bpy.context.scene['search results'] + self.scroll_offset += self.wcount * self.hcount + self.scroll_update() + + def scroll_down(self, widget): + sr = bpy.context.scene['search results'] + self.scroll_offset -= self.wcount * self.hcount + self.scroll_update() + + +def register(): + bpy.utils.register_class(BlenderKitAssetBarOperator) + + +def unregister(): + bpy.utils.unregister_class(BlenderKitAssetBarOperator) |