diff options
author | Vilem Duha <vilem.duha@gmail.com> | 2021-10-22 16:10:00 +0300 |
---|---|---|
committer | Vilem Duha <vilem.duha@gmail.com> | 2021-10-22 16:10:00 +0300 |
commit | be3f162ef7c966750535c6c03d1e84be81d0b846 (patch) | |
tree | 524bce1574f573dffc5e28f7d31764402243ccdb | |
parent | 259ab9a873cbd66d8dd57d2ec730bf77548f2a6f (diff) |
BlenderKit: fix asset bar thumbnail refresh
also sevearal small UI tweaks and fix of the asset-bar scaling when switching on toolbar/properties in 3d view
remove debug prints
-rw-r--r-- | blenderkit/__init__.py | 4 | ||||
-rw-r--r-- | blenderkit/asset_bar_op.py | 137 | ||||
-rw-r--r-- | blenderkit/bl_ui_widgets/bl_ui_button.py | 16 | ||||
-rw-r--r-- | blenderkit/download.py | 29 | ||||
-rw-r--r-- | blenderkit/ratings.py | 2 | ||||
-rw-r--r-- | blenderkit/search.py | 9 | ||||
-rw-r--r-- | blenderkit/ui_panels.py | 85 |
7 files changed, 158 insertions, 124 deletions
diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py index 698de633..4a25c9d6 100644 --- a/blenderkit/__init__.py +++ b/blenderkit/__init__.py @@ -365,8 +365,8 @@ class BlenderKitUIProps(PropertyGroup): highlight_margin: IntProperty(name="Highlight Margin", default=int(margin_def / 2), min=-10, max=256) bar_height: IntProperty(name="Bar Height", default=thumb_size_def + 2 * margin_def, min=-1, max=2048) - bar_x_offset: IntProperty(name="Bar X Offset", default=0, min=0, max=5000) - bar_y_offset: IntProperty(name="Bar Y Offset", default=60, min=0, max=5000) + bar_x_offset: IntProperty(name="Bar X Offset", default=40, min=0, max=5000) + bar_y_offset: IntProperty(name="Bar Y Offset", default=80, min=0, max=5000) bar_x: IntProperty(name="Bar X", default=100, min=0, max=5000) bar_y: IntProperty(name="Bar Y", default=100, min=50, max=5000) diff --git a/blenderkit/asset_bar_op.py b/blenderkit/asset_bar_op.py index 403eb039..d13c3767 100644 --- a/blenderkit/asset_bar_op.py +++ b/blenderkit/asset_bar_op.py @@ -13,6 +13,7 @@ from blenderkit.bl_ui_widgets.bl_ui_draw_op import * # from blenderkit.bl_ui_widgets.bl_ui_textbox import * import random import math +import time import blenderkit from blenderkit import ui, paths, utils, search, comments_utils @@ -47,7 +48,8 @@ def get_area_height(self): BL_UI_Widget.get_area_height = get_area_height -def modal_inside(self,context,event): + +def modal_inside(self, context, event): ui_props = bpy.context.window_manager.blenderkitUI if ui_props.turn_off: ui_props.turn_off = False @@ -62,20 +64,27 @@ def modal_inside(self,context,event): self.finish() return {'FINISHED'} - # progress bar - # Todo: put this into a timer? - sr = bpy.context.window_manager.get('search results') - ui_scale = bpy.context.preferences.view.ui_scale - for asset_button in self.asset_buttons: + self.update_timer +=1 + + if self.update_timer > self.update_timer_limit: + self.update_timer = 0 + # print('timer', time.time()) + self.update_images() + + # progress bar + sr = bpy.context.window_manager.get('search results') + ui_scale = bpy.context.preferences.view.ui_scale + for asset_button in self.asset_buttons: + if sr is not None and len(sr) > asset_button.asset_index: + asset_data = sr[asset_button.asset_index] + + if asset_data['downloaded'] > 0: + asset_button.progress_bar.width = int(self.button_size * ui_scale * asset_data['downloaded'] / 100) + asset_button.progress_bar.visible = True + else: + asset_button.progress_bar.visible = False - if sr is not None and len(sr) > asset_button.asset_index: - asset_data = sr[asset_button.asset_index] - if asset_data['downloaded'] > 0: - asset_button.progress_bar.width = int(self.button_size * ui_scale * asset_data['downloaded'] / 100) - asset_button.progress_bar.visible = True - else: - asset_button.progress_bar.visible = False if self.handle_widget_events(event): return {'RUNNING_MODAL'} @@ -85,12 +94,12 @@ def modal_inside(self,context,event): self.mouse_x = event.mouse_region_x self.mouse_y = event.mouse_region_y if event.type == 'WHEELUPMOUSE' and self.panel.is_in_rect(self.mouse_x, self.mouse_y): - self.scroll_offset -= 5 + self.scroll_offset -= 2 self.scroll_update() return {'RUNNING_MODAL'} elif event.type == 'WHEELDOWNMOUSE' and self.panel.is_in_rect(self.mouse_x, self.mouse_y): - self.scroll_offset += 5 + self.scroll_offset += 2 self.scroll_update() return {'RUNNING_MODAL'} @@ -101,8 +110,9 @@ def modal_inside(self,context,event): return {"PASS_THROUGH"} + def asset_bar_modal(self, context, event): - return modal_inside(self,context,event) + return modal_inside(self, context, event) def asset_bar_invoke(self, context, event): @@ -113,6 +123,11 @@ def asset_bar_invoke(self, context, event): self.register_handlers(args, context) + self.update_timer_limit = 30 + self.update_timer =0 + # print('adding timer') + # self._timer = context.window_manager.event_timer_add(10.0, window=context.window) + context.window_manager.modal_handler_add(self) return {"RUNNING_MODAL"} @@ -298,42 +313,34 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): return True return False - def check_ui_resized(self, context): - # TODO this should only check if region was resized, not really care about the UI elements size. - region = context.region - area = context.area - ui_props = bpy.context.window_manager.blenderkitUI - ui_scale = bpy.context.preferences.view.ui_scale + def get_region_size(self, context): # just check the size of region.. - if not hasattr(self, 'region_width'): - self.region_width = region.width - self.region_height = region.height - if region.height != self.region_height or region.width != self.region_width: - self.region_height = region.height - self.region_width = region.width - return True - return False - # this actually calculated UI elements, which is unnecessary - reg_multiplier = 1 - if not bpy.context.preferences.system.use_region_overlap: - reg_multiplier = 0 + region = context.region + area = context.area + ui_width = 0 + tools_width = 0 for r in area.regions: + if r.type == 'UI': + ui_width = r.width 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 + tools_width = r.width + total_width = region.width - tools_width - ui_width + return total_width, region.height - bar_width = region.width - self.bar_x - self.bar_end + def check_ui_resized(self, context): + # TODO this should only check if region was resized, not really care about the UI elements size. + region_width, region_height = self.get_region_size(context) - bar_y = ui_props.bar_y_offset * ui_scale + if not hasattr(self, 'total_width'): + self.total_width = region_width + self.region_height = region_height - changed = False - if bar_width != self.bar_width: - changed = True - if bar_y != self.bar_y: - changed = True - return changed + if region_height != self.region_height or region_width != self.total_width: + self.region_height = region_height + self.total_width = region_width + return True + return False def update_ui_size(self, context): @@ -364,12 +371,18 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): if not bpy.context.preferences.system.use_region_overlap: reg_multiplier = 0 + ui_width = 0 + tools_width = 0 + reg_multiplier = 1 + if not bpy.context.preferences.system.use_region_overlap: + reg_multiplier = 0 for r in area.regions: + if r.type == 'UI' : + ui_width = r.width * reg_multiplier 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 + 120 * ui_scale - + tools_width = r.width * reg_multiplier + self.bar_x = tools_width + self.margin + ui_props.bar_x_offset * ui_scale + self.bar_end = ui_width + 180 * ui_scale + self.other_button_size self.bar_width = region.width - self.bar_x - self.bar_end self.wcount = math.floor((self.bar_width) / (self.button_size)) @@ -402,13 +415,13 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): self.position_and_hide_buttons() self.button_close.set_location(self.bar_width - self.other_button_size, -self.other_button_size) - if hasattr(self,'button_notifications'): - self.button_notifications.set_location(self.bar_width - self.other_button_size*2, -self.other_button_size) + if hasattr(self, 'button_notifications'): + self.button_notifications.set_location(self.bar_width - self.other_button_size * 2, -self.other_button_size) self.button_scroll_up.set_location(self.bar_width, 0) self.panel.width = self.bar_width self.panel.height = self.bar_height - self.panel.set_location(self.panel.x, self.panel.y) + self.panel.set_location(self.bar_x, self.panel.y) # to hide arrows accordingly self.scroll_update() @@ -460,8 +473,6 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): new_button.progress_bar = progress_bar self.progress_bars.append(progress_bar) - - # if result['downloaded'] > 0: # ui_bgl.draw_rect(x, y, int(ui_props.thumb_size * result['downloaded'] / 100.0), 2, green) @@ -498,7 +509,8 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): self.asset_buttons.append(new_button) button_idx += 1 - self.button_close = BL_UI_Button(self.bar_width - self.other_button_size, -self.other_button_size, self.other_button_size, + self.button_close = BL_UI_Button(self.bar_width - self.other_button_size, -self.other_button_size, + self.other_button_size, self.other_button_size) self.button_close.bg_color = button_bg_color self.button_close.hover_bg_color = button_hover_color @@ -534,11 +546,11 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): self.widgets_panel.append(self.button_scroll_up) - #notifications + # notifications if not comments_utils.check_notifications_read(): - - self.button_notifications = BL_UI_Button(self.bar_width - self.other_button_size*2, -self.other_button_size, self.other_button_size, - self.other_button_size) + self.button_notifications = BL_UI_Button(self.bar_width - self.other_button_size * 2, + -self.other_button_size, self.other_button_size, + self.other_button_size) self.button_notifications.bg_color = button_bg_color self.button_notifications.hover_bg_color = button_hover_color self.button_notifications.text = "" @@ -562,7 +574,7 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): asset_y = self.assetbar_margin + y * (self.button_size) button_idx = x + y * self.wcount asset_idx = button_idx + self.scroll_offset - if len(self.asset_buttons)<=button_idx: + if len(self.asset_buttons) <= button_idx: break button = self.asset_buttons[button_idx] button.set_location(asset_x, asset_y) @@ -681,6 +693,8 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): global asset_bar_operator asset_bar_operator = None + # context.window_manager.event_timer_remove(self._timer) + scene = bpy.context.scene ui_props = bpy.context.window_manager.blenderkitUI ui_props.assetbar_on = False @@ -752,7 +766,7 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): ui_props.draw_tooltip = False self.draw_tooltip = False self.hide_tooltip() - #popup asset card on mouse down + # popup asset card on mouse down # if utils.experimental_enabled(): # h = widget.get_area_height() # print(h,h-self.mouse_y,self.panel.y_screen, self.panel.y,widget.y_screen, widget.y) @@ -818,6 +832,7 @@ class BlenderKitAssetBarOperator(BL_UI_OT_draw_operator): img_filepath = paths.get_addon_thumbnail_path('thumbnail_notready.jpg') else: img_filepath = img.filepath + # print(asset_button.button_index, img_filepath) asset_button.set_image(img_filepath) self.update_validation_icon(asset_button, asset_data) diff --git a/blenderkit/bl_ui_widgets/bl_ui_button.py b/blenderkit/bl_ui_widgets/bl_ui_button.py index d8772e68..db81b9f8 100644 --- a/blenderkit/bl_ui_widgets/bl_ui_button.py +++ b/blenderkit/bl_ui_widgets/bl_ui_button.py @@ -67,12 +67,24 @@ class BL_UI_Button(BL_UI_Widget): self.__image_position = image_position def set_image(self, rel_filepath): + #first try to access the image, for cases where it can get removed + try: + self.__image + self.__image.filepath + self.__image.pixels + except: + self.__image = None try: if self.__image is None or self.__image.filepath != rel_filepath: self.__image = bpy.data.images.load(rel_filepath, check_existing=True) self.__image.gl_load() - except: - pass + + if self.__image and len(self.__image.pixels) == 0: + self.__image.reload() + self.__image.gl_load() + + except Exception as e: + self.__image = None def update(self, x, y): super().update(x, y) diff --git a/blenderkit/download.py b/blenderkit/download.py index e6f88634..9fb48073 100644 --- a/blenderkit/download.py +++ b/blenderkit/download.py @@ -75,7 +75,7 @@ def check_missing(): def check_unused(): '''find assets that have been deleted from scene but their library is still present.''' # this is obviously broken. Blender should take care of the extra data automaticlaly - #first clean up collections + # first clean up collections for c in bpy.data.collections: if len(c.all_objects) == 0 and c.get('is_blenderkit_asset'): bpy.data.collections.remove(c) @@ -331,7 +331,6 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None, props = hdr.blenderkit asset_main = hdr - if asset_data['assetType'] == 'model': downloaders = kwargs.get('downloaders') sprops = wm.blenderkit_models @@ -498,8 +497,6 @@ def append_asset(asset_data, **kwargs): # downloaders=[], location=None, asset_main.blenderkit.asset_base_id = asset_data['assetBaseId'] asset_main.blenderkit.id = asset_data['id'] - - bpy.ops.wm.undo_push_context(message='add %s to scene' % asset_data['name']) # moving reporting to on save. # report_use_success(asset_data['id']) @@ -599,7 +596,7 @@ def download_timer(): if sr is not None: for r in sr: if asset_data['id'] == r['id']: - r['downloaded'] = 0.5#tcom.progress + r['downloaded'] = 0.5 # tcom.progress if not t.is_alive(): if tcom.error: sprops = utils.get_search_props() @@ -698,7 +695,7 @@ def delete_unfinished_file(file_name): return -def download_asset_file(asset_data, resolution='blend', api_key = ''): +def download_asset_file(asset_data, resolution='blend', api_key=''): # this is a simple non-threaded way to download files for background resolution genenration tool file_names = paths.get_download_filepaths(asset_data, resolution) # prefer global dir if possible. if len(file_names) == 0: @@ -854,7 +851,6 @@ class Downloader(threading.Thread): # utils.p('end downloader thread') - class ThreadCom: # object passed to threads to read background process stdout info def __init__(self): self.file_size = 1000000000000000 # property that gets written to. @@ -1279,7 +1275,7 @@ class BlenderkitDownloadOperator(bpy.types.Operator): invoke_resolution: BoolProperty(name='Replace resolution popup', description='pop up to ask which resolution to download', default=False) invoke_scene_settings: BoolProperty(name='Scene import settings popup', - description='pop up scene import settings', default=False) + description='pop up scene import settings', default=False) resolution: EnumProperty( items=available_resolutions_callback, @@ -1287,7 +1283,7 @@ class BlenderkitDownloadOperator(bpy.types.Operator): description='Replace resolution' ) - #needs to be passed to the operator to not show all resolution possibilities + # needs to be passed to the operator to not show all resolution possibilities max_resolution: IntProperty( name="Max resolution", description="", @@ -1306,6 +1302,13 @@ class BlenderkitDownloadOperator(bpy.types.Operator): # @classmethod # def poll(cls, context): # return bpy.context.window_manager.BlenderKitModelThumbnails is not '' + tooltip: bpy.props.StringProperty( + default='Download and link asset to scene. Only link if asset already available locally') + + @classmethod + def description(cls, context, properties): + return properties.tooltip + def get_asset_data(self, context): # get asset data - it can come from scene, or from search results. s = bpy.context.scene @@ -1327,14 +1330,14 @@ class BlenderkitDownloadOperator(bpy.types.Operator): # already used assets have already download link and especially file link. asset_data = s['assets used'][asset_base_id].to_dict() else: - #when not in scene nor in search results, we need to get it from the server + # when not in scene nor in search results, we need to get it from the server params = { 'asset_base_id': self.asset_base_id } preferences = bpy.context.preferences.addons['blenderkit'].preferences - results = search.get_search_simple(params, page_size=1, max_results=1, - api_key=preferences.api_key) + results = search.get_search_simple(params, page_size=1, max_results=1, + api_key=preferences.api_key) asset_data = search.parse_result(results[0]) return asset_data @@ -1426,7 +1429,7 @@ class BlenderkitDownloadOperator(bpy.types.Operator): self.asset_data = self.get_asset_data(context) sprops = utils.get_search_props() - #set initial resolutions enum activation + # set initial resolutions enum activation if sprops.resolution != 'ORIGINAL' and int(sprops.resolution) <= int(self.max_resolution): self.resolution = sprops.resolution elif int(self.max_resolution) > 0: diff --git a/blenderkit/ratings.py b/blenderkit/ratings.py index be9719ae..3baabb5b 100644 --- a/blenderkit/ratings.py +++ b/blenderkit/ratings.py @@ -278,7 +278,7 @@ def rating_menu_draw(self, context): layout.label(text='Admin rating Tools:') col.operator_context = 'INVOKE_DEFAULT' - op = col.operator('wm.blenderkit_menu_rating_upload', text='Rate') + op = col.operator('wm.blenderkit_menu_rating_upload', text='Add Rating') op.asset_id = asset_data['id'] op.asset_name = asset_data['name'] op.asset_type = asset_data['assetType'] diff --git a/blenderkit/search.py b/blenderkit/search.py index c6dd57e3..0464f511 100644 --- a/blenderkit/search.py +++ b/blenderkit/search.py @@ -391,13 +391,11 @@ def search_timer(): ui_props = bpy.context.window_manager.blenderkitUI search_name = f'bkit {ui_props.asset_type.lower()} search' wm = bpy.context.window_manager - print('finishing reading thumbs') if wm.get(search_name) is not None: all_loaded = True for ri, r in enumerate(wm[search_name]): if not r.get('thumb_small_loaded'): preview_loaded = load_preview(r, ri) - print(ri, preview_loaded, all_thumbs_loaded) all_loaded = all_loaded and preview_loaded all_thumbs_loaded = all_loaded @@ -452,7 +450,6 @@ def search_timer(): if ok: ui_props = bpy.context.window_manager.blenderkitUI orig_len = len(result_field) - print('reading from thread') for ri, r in enumerate(rdata['results']): asset_data = parse_result(r) @@ -526,9 +523,13 @@ def load_preview(asset, index): # wrap into try statement since sometimes try: img = bpy.data.images.load(tpath) + img.name = iname + if len(img.pixels)>0: + return True except: - return False + pass + return False elif img.filepath != tpath: if not os.path.exists(tpath): # unload loaded previews from previous results diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py index 92794773..add64653 100644 --- a/blenderkit/ui_panels.py +++ b/blenderkit/ui_panels.py @@ -1310,7 +1310,7 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False): layout.operator_context = 'INVOKE_DEFAULT' if from_panel: - op = layout.operator('wm.blenderkit_menu_rating_upload', text='Rate') + op = layout.operator('wm.blenderkit_menu_rating_upload', text='Add Rating') op.asset_name = asset_data['name'] op.asset_id = asset_data['id'] op.asset_type = asset_data['assetType'] @@ -1343,6 +1343,7 @@ def draw_asset_context_menu(layout, context, asset_data, from_panel=False): if aob is None: aob = bpy.context.selected_objects[0] op = layout.operator('scene.blenderkit_download', text='Replace Active Models') + op.tooltip = "Replace all selected models with this one." # this checks if the menu got called from right-click in assetbar(then index is 0 - x) or # from a panel(then replacement happens from the active model) @@ -2391,53 +2392,55 @@ def header_search_draw(self, context): return; preferences = bpy.context.preferences.addons['blenderkit'].preferences - if preferences.search_in_header: - layout = self.layout - s = bpy.context.scene - wm = bpy.context.window_manager - ui_props = bpy.context.window_manager.blenderkitUI - if ui_props.asset_type == 'MODEL': - props = wm.blenderkit_models - if ui_props.asset_type == 'MATERIAL': - props = wm.blenderkit_mat - if ui_props.asset_type == 'BRUSH': - props = wm.blenderkit_brush - if ui_props.asset_type == 'HDR': - props = wm.blenderkit_HDR - if ui_props.asset_type == 'SCENE': - props = wm.blenderkit_scene + if not preferences.search_in_header: + return - # the center snap menu is in edit and object mode if tool settings are off. - if context.space_data.show_region_tool_header == True or context.mode[:4] not in ('EDIT', 'OBJE'): - layout.separator_spacer() - layout.prop(ui_props, "asset_type", expand=True, icon_only=True, text='', icon='URL') - layout.prop(props, "search_keywords", text="", icon='VIEWZOOM') - draw_assetbar_show_hide(layout, props) - layout.popover(panel="VIEW3D_PT_blenderkit_categories", text="", icon='OUTLINER') + layout = self.layout + s = bpy.context.scene + wm = bpy.context.window_manager + ui_props = bpy.context.window_manager.blenderkitUI + if ui_props.asset_type == 'MODEL': + props = wm.blenderkit_models + if ui_props.asset_type == 'MATERIAL': + props = wm.blenderkit_mat + if ui_props.asset_type == 'BRUSH': + props = wm.blenderkit_brush + if ui_props.asset_type == 'HDR': + props = wm.blenderkit_HDR + if ui_props.asset_type == 'SCENE': + props = wm.blenderkit_scene - pcoll = icons.icon_collections["main"] + # the center snap menu is in edit and object mode if tool settings are off. + if context.space_data.show_region_tool_header == True or context.mode[:4] not in ('EDIT', 'OBJE'): + layout.separator_spacer() + layout.prop(ui_props, "asset_type", expand=True, icon_only=True, text='', icon='URL') + layout.prop(props, "search_keywords", text="", icon='VIEWZOOM') + draw_assetbar_show_hide(layout, props) + layout.popover(panel="VIEW3D_PT_blenderkit_categories", text="", icon='OUTLINER') - if props.use_filters: - icon_id = pcoll['filter_active'].icon_id - else: - icon_id = pcoll['filter'].icon_id + pcoll = icons.icon_collections["main"] - if ui_props.asset_type == 'MODEL': - layout.popover(panel="VIEW3D_PT_blenderkit_advanced_model_search", text="", icon_value=icon_id) + if props.use_filters: + icon_id = pcoll['filter_active'].icon_id + else: + icon_id = pcoll['filter'].icon_id - elif ui_props.asset_type == 'MATERIAL': - layout.popover(panel="VIEW3D_PT_blenderkit_advanced_material_search", text="", icon_value=icon_id) - elif ui_props.asset_type == 'HDR': - layout.popover(panel="VIEW3D_PT_blenderkit_advanced_HDR_search", text="", icon_value=icon_id) + if ui_props.asset_type == 'MODEL': + layout.popover(panel="VIEW3D_PT_blenderkit_advanced_model_search", text="", icon_value=icon_id) - notifications = bpy.context.window_manager.get('bkit notifications') - if notifications is not None and len(notifications) > 0: - layout.operator('wm.show_notifications', text="", icon_value=pcoll['bell'].icon_id) - # layout.popover(panel="VIEW3D_PT_blenderkit_notifications", text="", icon_value=pcoll['bell'].icon_id) + elif ui_props.asset_type == 'MATERIAL': + layout.popover(panel="VIEW3D_PT_blenderkit_advanced_material_search", text="", icon_value=icon_id) + elif ui_props.asset_type == 'HDR': + layout.popover(panel="VIEW3D_PT_blenderkit_advanced_HDR_search", text="", icon_value=icon_id) - if utils.profile_is_validator(): - search_props = utils.get_search_props() - layout.prop(search_props, 'search_verification_status', text='') + notifications = bpy.context.window_manager.get('bkit notifications') + if notifications is not None and len(notifications) > 0: + layout.operator('wm.show_notifications', text="", icon_value=pcoll['bell'].icon_id) + # layout.popover(panel="VIEW3D_PT_blenderkit_notifications", text="", icon_value=pcoll['bell'].icon_id) + + if utils.profile_is_validator(): + search_props = utils.get_search_props() + layout.prop(search_props, 'search_verification_status', text='') def ui_message(title, message): |