diff --git a/blenderkit/__init__.py b/blenderkit/__init__.py
index 69fcb8b61368c732274b2fbe0ebd08096e4e46ad..d3cd5a724bc00256b0c93bec21052b77f2bb8a7d 100644
--- a/blenderkit/__init__.py
+++ b/blenderkit/__init__.py
@@ -34,6 +34,7 @@ if "bpy" in locals():
     # modules with _bg are used for background computations in separate blender instance and that's why they don't need reload.
 
     append_link = reload(append_link)
+    asset_bar_op = reload(asset_bar_op)
     asset_inspector = reload(asset_inspector)
     autothumb = reload(autothumb)
     bg_blender = reload(bg_blender)
@@ -55,8 +56,19 @@ if "bpy" in locals():
     ui_panels = reload(ui_panels)
     upload = reload(upload)
     utils = reload(utils)
+
+    bl_ui_label = reload(bl_ui_label)
+    bl_ui_button = reload(bl_ui_button)
+    # bl_ui_checkbox = reload(bl_ui_checkbox)
+    # bl_ui_slider = reload(bl_ui_slider)
+    # bl_ui_up_down = reload(bl_ui_up_down)
+    bl_ui_drag_panel = reload(bl_ui_drag_panel)
+    bl_ui_draw_op = reload(bl_ui_draw_op)
+    # bl_ui_textbox = reload(bl_ui_textbox)
+
 else:
     from blenderkit import append_link
+    from blenderkit import asset_bar_op
     from blenderkit import asset_inspector
     from blenderkit import autothumb
     from blenderkit import bg_blender
@@ -79,6 +91,15 @@ else:
     from blenderkit import upload
     from blenderkit import utils
 
+    from blenderkit.bl_ui_widgets import bl_ui_label 
+    from blenderkit.bl_ui_widgets import bl_ui_button 
+    # from blenderkit.bl_ui_widgets import bl_ui_checkbox
+    # from blenderkit.bl_ui_widgets import bl_ui_slider
+    # from blenderkit.bl_ui_widgets import bl_ui_up_down
+    from blenderkit.bl_ui_widgets import bl_ui_drag_panel 
+    from blenderkit.bl_ui_widgets import bl_ui_draw_op 
+    # from blenderkit.bl_ui_widgets import bl_ui_textbox
+
 
 import os
 import math
@@ -1787,6 +1808,7 @@ def register():
     overrides.register_overrides()
     bkit_oauth.register()
     tasks_queue.register()
+    asset_bar_op.register()
 
     user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
     if user_preferences.use_timers:
@@ -1818,6 +1840,7 @@ def unregister():
     overrides.unregister_overrides()
     bkit_oauth.unregister()
     tasks_queue.unregister()
+    asset_bar_op.unregister()
 
     del bpy.types.Scene.blenderkit_models
     del bpy.types.Scene.blenderkit_scene
diff --git a/blenderkit/asset_bar_op.py b/blenderkit/asset_bar_op.py
new file mode 100644
index 0000000000000000000000000000000000000000..612f588528a0aee60783f188c6f2fa20fc7a0cde
--- /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)
diff --git a/blenderkit/bl_ui_widgets/__init__.py b/blenderkit/bl_ui_widgets/__init__.py
new file mode 100644
index 0000000000000000000000000000000000000000..745f59c928e0129e4933278f35b467976c9b7ff8
--- /dev/null
+++ b/blenderkit/bl_ui_widgets/__init__.py
@@ -0,0 +1,36 @@
+bl_info = {
+    "name": "BL UI Widgets",
+    "description": "UI Widgets to draw in the 3D view",
+    "author": "Jayanam",
+    "version": (0, 6, 4, 2),
+    "blender": (2, 80, 0),
+    "location": "View3D",
+    "category": "Object"}
+
+# Blender imports
+import bpy
+
+from bpy.props import *
+
+
+addon_keymaps = []
+
+def register():
+    
+    bpy.utils.register_class(DP_OT_draw_operator)
+    kcfg = bpy.context.window_manager.keyconfigs.addon
+    if kcfg:
+        km = kcfg.keymaps.new(name='3D View', space_type='VIEW_3D')
+
+
+        addon_keymaps.append((km, kmi))
+
+def unregister():
+    for km, kmi in addon_keymaps:
+        km.keymap_items.remove(kmi)
+    addon_keymaps.clear()
+   
+    bpy.utils.unregister_class(DP_OT_draw_operator)
+    
+if __name__ == "__main__":
+    register()
diff --git a/blenderkit/bl_ui_widgets/bl_ui_button.py b/blenderkit/bl_ui_widgets/bl_ui_button.py
new file mode 100644
index 0000000000000000000000000000000000000000..ce26c54ef9d720f9a276f9e2d62213a0cf2ac828
--- /dev/null
+++ b/blenderkit/bl_ui_widgets/bl_ui_button.py
@@ -0,0 +1,192 @@
+from . bl_ui_widget import *
+
+import blf
+import bpy
+
+class BL_UI_Button(BL_UI_Widget):
+    
+    def __init__(self, x, y, width, height):
+        super().__init__(x, y, width, height)
+        self._text_color        = (1.0, 1.0, 1.0, 1.0)
+        self._hover_bg_color    = (0.5, 0.5, 0.5, 1.0)
+        self._select_bg_color   = (0.7, 0.7, 0.7, 1.0)
+        
+        self._text = "Button"
+        self._text_size = 16
+        self._textpos = (x, y)
+
+        self.__state = 0
+        self.__image = None
+        self.__image_size = (24, 24)
+        self.__image_position = (4, 2)
+
+    @property
+    def text_color(self):
+        return self._text_color
+
+    @text_color.setter
+    def text_color(self, value):
+        self._text_color = value
+
+    @property
+    def text(self):
+        return self._text
+
+    @text.setter
+    def text(self, value):
+        self._text = value
+                
+    @property
+    def text_size(self):
+        return self._text_size
+
+    @text_size.setter
+    def text_size(self, value):
+        self._text_size = value
+
+    @property
+    def hover_bg_color(self):
+        return self._hover_bg_color
+
+    @hover_bg_color.setter
+    def hover_bg_color(self, value):
+        self._hover_bg_color = value
+
+    @property
+    def select_bg_color(self):
+        return self._select_bg_color
+
+    @select_bg_color.setter
+    def select_bg_color(self, value):
+        self._select_bg_color = value 
+        
+    def set_image_size(self, imgage_size):
+        self.__image_size = imgage_size
+
+    def set_image_position(self, image_position):
+        self.__image_position = image_position
+
+    def set_image(self, rel_filepath):
+        try:
+            self.__image = bpy.data.images.load(rel_filepath, check_existing=True)   
+            self.__image.gl_load()
+        except:
+            pass
+
+    def update(self, x, y):        
+        super().update(x, y)
+        self._textpos = [x, y]
+        
+    def draw(self):
+        if not self.visible:
+            return
+            
+        area_height = self.get_area_height()
+
+        self.shader.bind()
+        
+        self.set_colors()
+        
+        bgl.glEnable(bgl.GL_BLEND)
+
+        self.batch_panel.draw(self.shader) 
+
+        self.draw_image()   
+
+        bgl.glDisable(bgl.GL_BLEND)
+
+        # Draw text
+        self.draw_text(area_height)
+
+    def set_colors(self):
+        color = self._bg_color
+        text_color = self._text_color
+
+        # pressed
+        if self.__state == 1:
+            color = self._select_bg_color
+
+        # hover
+        elif self.__state == 2:
+            color = self._hover_bg_color
+
+        self.shader.uniform_float("color", color)
+
+    def draw_text(self, area_height):
+        blf.size(0, self._text_size, 72)
+        size = blf.dimensions(0, self._text)
+
+        textpos_y = area_height - self._textpos[1] - (self.height + size[1]) / 2.0
+        blf.position(0, self._textpos[0] + (self.width - size[0]) / 2.0, textpos_y + 1, 0)
+
+        r, g, b, a = self._text_color
+        blf.color(0, r, g, b, a)
+
+        blf.draw(0, self._text)
+
+    def draw_image(self):
+        if self.__image is not None:
+            try:
+                y_screen_flip = self.get_area_height() - self.y_screen
+        
+                off_x, off_y =  self.__image_position
+                sx, sy = self.__image_size
+                
+                # bottom left, top left, top right, bottom right
+                vertices = (
+                            (self.x_screen + off_x, y_screen_flip - off_y), 
+                            (self.x_screen + off_x, y_screen_flip - sy - off_y), 
+                            (self.x_screen + off_x + sx, y_screen_flip - sy - off_y),
+                            (self.x_screen + off_x + sx, y_screen_flip - off_y))
+                
+                self.shader_img = gpu.shader.from_builtin('2D_IMAGE')
+                self.batch_img = batch_for_shader(self.shader_img, 'TRI_FAN', 
+                { "pos" : vertices, 
+                "texCoord": ((0, 1), (0, 0), (1, 0), (1, 1)) 
+                },)
+
+                # send image to gpu if it isn't there already
+                if self.__image.gl_load():
+                    raise Exception()
+
+                bgl.glActiveTexture(bgl.GL_TEXTURE0)
+                bgl.glBindTexture(bgl.GL_TEXTURE_2D, self.__image.bindcode)
+
+                self.shader_img.bind()
+                self.shader_img.uniform_int("image", 0)
+                self.batch_img.draw(self.shader_img) 
+                return True
+            except:
+                pass
+
+        return False     
+        
+    def set_mouse_down(self, mouse_down_func):
+        self.mouse_down_func = mouse_down_func   
+                 
+    def mouse_down(self, x, y):    
+        if self.is_in_rect(x,y):
+            self.__state = 1
+            try:
+                self.mouse_down_func(self)
+            except Exception as e:
+                print(e)
+                
+            return True
+        
+        return False
+    
+    def mouse_move(self, x, y):
+        if self.is_in_rect(x,y):
+            if(self.__state != 1):
+                
+                # hover state
+                self.__state = 2
+        else:
+            self.__state = 0
+ 
+    def mouse_up(self, x, y):
+        if self.is_in_rect(x,y):
+            self.__state = 2
+        else:
+            self.__state = 0
\ No newline at end of file
diff --git a/blenderkit/bl_ui_widgets/bl_ui_drag_panel.py b/blenderkit/bl_ui_widgets/bl_ui_drag_panel.py
new file mode 100644
index 0000000000000000000000000000000000000000..e07aa784b9a6415e98b16a079f53c100467930ed
--- /dev/null
+++ b/blenderkit/bl_ui_widgets/bl_ui_drag_panel.py
@@ -0,0 +1,58 @@
+from . bl_ui_widget import * 
+
+class BL_UI_Drag_Panel(BL_UI_Widget):
+    
+    def __init__(self, x, y, width, height):
+        super().__init__(x,y, width, height)
+        self.drag_offset_x = 0
+        self.drag_offset_y = 0
+        self.is_drag = False
+        self.widgets = []
+
+    def set_location(self, x, y):
+        super().set_location(x,y)
+        self.layout_widgets()
+
+    def add_widget(self, widget):
+        self.widgets.append(widget)
+        
+    def add_widgets(self, widgets):
+        self.widgets = widgets
+        self.layout_widgets()
+        
+    def layout_widgets(self):
+        for widget in self.widgets:
+            widget.update(self.x_screen + widget.x, self.y_screen + widget.y)   
+    
+    def update(self, x, y):
+        super().update(x - self.drag_offset_x, y + self.drag_offset_y)
+    
+    def child_widget_focused(self, x, y):
+        for widget in self.widgets:
+            if widget.is_in_rect(x, y):
+                return True       
+        return False
+    
+    def mouse_down(self, x, y):
+        if self.child_widget_focused(x, y):
+            return False
+        
+        if self.is_in_rect(x,y):
+            height = self.get_area_height()
+            self.is_drag = True
+            self.drag_offset_x = x - self.x_screen
+            self.drag_offset_y = y - (height - self.y_screen)
+            return True
+        
+        return False
+
+    def mouse_move(self, x, y):
+        if self.is_drag:
+            height = self.get_area_height()
+            self.update(x, height - y)
+            self.layout_widgets()
+
+    def mouse_up(self, x, y):
+        self.is_drag = False
+        self.drag_offset_x = 0
+        self.drag_offset_y = 0
\ No newline at end of file
diff --git a/blenderkit/bl_ui_widgets/bl_ui_draw_op.py b/blenderkit/bl_ui_widgets/bl_ui_draw_op.py
new file mode 100644
index 0000000000000000000000000000000000000000..2f53447929d744a12fa9e3065a69ea80465b5ea3
--- /dev/null
+++ b/blenderkit/bl_ui_widgets/bl_ui_draw_op.py
@@ -0,0 +1,83 @@
+import bpy
+
+from bpy.types import Operator
+
+class BL_UI_OT_draw_operator(Operator):
+    bl_idname = "object.bl_ui_ot_draw_operator"
+    bl_label = "bl ui widgets operator"
+    bl_description = "Operator for bl ui widgets" 
+    bl_options = {'REGISTER'}
+    	
+    def __init__(self):
+        self.draw_handle = None
+        self.draw_event  = None
+        self._finished = False
+                
+        self.widgets = []
+
+    def init_widgets(self, context, widgets):
+        self.widgets = widgets
+        for widget in self.widgets:
+            widget.init(context)
+
+    def on_invoke(self, context, event):
+        pass
+
+    def on_finish(self, context):
+        self._finished = True
+
+    def invoke(self, context, event):
+
+        self.on_invoke(context, event)
+
+        args = (self, context)
+                   
+        self.register_handlers(args, context)
+                   
+        context.window_manager.modal_handler_add(self)
+        return {"RUNNING_MODAL"}
+    
+    def register_handlers(self, args, context):
+        self.draw_handle = bpy.types.SpaceView3D.draw_handler_add(self.draw_callback_px, args, "WINDOW", "POST_PIXEL")
+        self.draw_event = context.window_manager.event_timer_add(0.1, window=context.window)
+        
+    def unregister_handlers(self, context):
+        
+        context.window_manager.event_timer_remove(self.draw_event)
+        
+        bpy.types.SpaceView3D.draw_handler_remove(self.draw_handle, "WINDOW")
+        
+        self.draw_handle = None
+        self.draw_event  = None
+        
+    def handle_widget_events(self, event):
+        result = False
+        for widget in self.widgets:
+            if widget.handle_event(event):
+                result = True
+        return result
+          
+    def modal(self, context, event):
+
+        if self._finished:
+            return {'FINISHED'}
+
+        if context.area:
+            context.area.tag_redraw()
+        
+        if self.handle_widget_events(event):
+            return {'RUNNING_MODAL'}   
+        
+        if event.type in {"ESC"}:
+            self.finish()
+                    
+        return {"PASS_THROUGH"}
+                                
+    def finish(self):
+        self.unregister_handlers(bpy.context)
+        self.on_finish(bpy.context)
+		
+	# Draw handler to paint onto the screen
+    def draw_callback_px(self, op, context):
+        for widget in self.widgets:
+            widget.draw()
\ No newline at end of file
diff --git a/blenderkit/bl_ui_widgets/bl_ui_label.py b/blenderkit/bl_ui_widgets/bl_ui_label.py
new file mode 100644
index 0000000000000000000000000000000000000000..11743b41ffceea9ad9bb01d99eda9c62aee34c7e
--- /dev/null
+++ b/blenderkit/bl_ui_widgets/bl_ui_label.py
@@ -0,0 +1,57 @@
+from . bl_ui_widget import *
+
+import blf
+
+class BL_UI_Label(BL_UI_Widget):
+    
+    def __init__(self, x, y, width, height):
+        super().__init__(x, y, width, height)
+
+        self._text_color        = (1.0, 1.0, 1.0, 1.0)
+        self._text = "Label"
+        self._text_size = 16
+
+    @property
+    def text_color(self):
+        return self._text_color
+
+    @text_color.setter
+    def text_color(self, value):
+        self._text_color = value
+
+    @property
+    def text(self):
+        return self._text
+
+    @text.setter
+    def text(self, value):
+        self._text = value
+
+    @property
+    def text_size(self):
+        return self._text_size
+
+    @text_size.setter
+    def text_size(self, value):
+        self._text_size = value
+            
+    def is_in_rect(self, x, y):
+        return False
+        
+    def draw(self):
+        if not self.visible:
+            return
+            
+        area_height = self.get_area_height()
+
+        blf.size(0, self._text_size, 72)
+        size = blf.dimensions(0, self._text)
+    
+        textpos_y = area_height - self.y_screen - self.height
+        blf.position(0, self.x_screen, textpos_y, 0)
+
+        r, g, b, a = self._text_color
+
+        blf.color(0, r, g, b, a)
+            
+        blf.draw(0, self._text)
\ No newline at end of file
diff --git a/blenderkit/bl_ui_widgets/bl_ui_widget.py b/blenderkit/bl_ui_widgets/bl_ui_widget.py
new file mode 100644
index 0000000000000000000000000000000000000000..2ae529fab4300016bec94d24d77061ceb6e60325
--- /dev/null
+++ b/blenderkit/bl_ui_widgets/bl_ui_widget.py
@@ -0,0 +1,189 @@
+import gpu
+import bgl
+
+from gpu_extras.batch import batch_for_shader
+
+class BL_UI_Widget:
+    
+    def __init__(self, x, y, width, height):
+        self.x = x
+        self.y = y
+        self.x_screen = x
+        self.y_screen = y
+        self.width = width
+        self.height = height
+        self._bg_color = (0.8, 0.8, 0.8, 1.0)
+        self._tag = None
+        self.context = None
+        self.__inrect = False
+        self._mouse_down = False
+        self._mouse_down_right = False
+        self._is_visible = True
+
+    def set_location(self, x, y):
+        self.x = x
+        self.y = y
+        self.x_screen = x
+        self.y_screen = y
+        self.update(x,y)
+
+    @property
+    def bg_color(self):
+        return self._bg_color
+
+    @bg_color.setter
+    def bg_color(self, value):
+        self._bg_color = value
+
+    @property
+    def visible(self):
+        return self._is_visible
+
+    @visible.setter
+    def visible(self, value):
+        self._is_visible = value
+
+    @property
+    def tag(self):
+        return self._tag
+
+    @tag.setter
+    def tag(self, value):
+        self._tag = value
+                		    
+    def draw(self):
+        if not self.visible:
+            return
+            
+        self.shader.bind()
+        self.shader.uniform_float("color", self._bg_color)
+        
+        bgl.glEnable(bgl.GL_BLEND)
+        self.batch_panel.draw(self.shader) 
+        bgl.glDisable(bgl.GL_BLEND)
+
+    def init(self, context):
+        self.context = context
+        self.update(self.x, self.y)
+    
+    def update(self, x, y):
+        
+        area_height = self.get_area_height()
+        
+        self.x_screen = x
+        self.y_screen = y
+                
+        indices = ((0, 1, 2), (0, 2, 3))
+
+        y_screen_flip = area_height - self.y_screen
+
+        # bottom left, top left, top right, bottom right
+        vertices = (
+                    (self.x_screen, y_screen_flip), 
+                    (self.x_screen, y_screen_flip - self.height), 
+                    (self.x_screen + self.width, y_screen_flip - self.height),
+                    (self.x_screen + self.width, y_screen_flip))
+                    
+        self.shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')
+        self.batch_panel = batch_for_shader(self.shader, 'TRIS', {"pos" : vertices}, indices=indices)
+
+    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
+
+    def get_input_keys(self)                :
+        return []
+
+    def get_area_height(self):
+        return self.context.area.height
+
+    def is_in_rect(self, x, y):
+        area_height = self.get_area_height()
+
+        widget_y = area_height - self.y_screen
+        if (
+            (self.x_screen <= x <= (self.x_screen + self.width)) and 
+            (widget_y >= y >= (widget_y - self.height))
+            ):
+            return True
+           
+        return False      
+
+    def text_input(self, event):       
+        return False
+
+    def mouse_down(self, x, y):       
+        return self.is_in_rect(x,y)
+
+    def mouse_down_right(self, x, y):
+        return self.is_in_rect(x,y)
+
+    def mouse_up(self, x, y):
+        pass
+
+    def set_mouse_enter(self, mouse_enter_func):
+        self.mouse_enter_func = mouse_enter_func  
+ 
+    def call_mouse_enter(self):
+        try:
+            if self.mouse_enter_func:
+                self.mouse_enter_func(self)
+        except:
+            pass
+
+    def mouse_enter(self, event, x, y):
+        self.call_mouse_enter()
+
+    def set_mouse_exit(self, mouse_exit_func):
+        self.mouse_exit_func = mouse_exit_func  
+ 
+    def call_mouse_exit(self):
+        try:
+            if self.mouse_exit_func:
+                self.mouse_exit_func(self)
+        except:
+            pass
+
+    def mouse_exit(self, event, x, y):
+        self.call_mouse_exit()
+
+    def mouse_move(self, x, y):
+        pass
\ No newline at end of file
diff --git a/blenderkit/colors.py b/blenderkit/colors.py
index c61d9fa0001e090065f617bee125c266ac9c5882..fe2fb1ac96148c2e889edbf929368162f66c6d53 100644
--- a/blenderkit/colors.py
+++ b/blenderkit/colors.py
@@ -18,6 +18,8 @@
 
 # this module defines color palette for BlenderKit UI
 
+WHITE = (1, 1, 1, .9)
+
 TEXT = (.9, .9, .9, .6)
 GREEN = (.9, 1, .9, .6)
 RED = (1, .5, .5, .8)
diff --git a/blenderkit/icons.py b/blenderkit/icons.py
index 3c6cea4b039f2ea24c2211b32dd35aa27364cc45..5d877b253b7cb1e8d13bfa8d1c76d0f7ce168247 100644
--- a/blenderkit/icons.py
+++ b/blenderkit/icons.py
@@ -27,6 +27,7 @@ icon_collections = {}
 icons_read = {
     'fp.png': 'free',
     'flp.png': 'full',
+    'test.jpg': 'test',
 }
 
 
@@ -44,6 +45,11 @@ def register_icons():
     for ir in icons_read.keys():
         pcoll.load(icons_read[ir], os.path.join(icons_dir, ir), 'IMAGE')
 
+        # iprev = pcoll.new(icons_read[ir])
+        # img = bpy.data.images.load(os.path.join(icons_dir, ir))
+        # iprev.image_size = (img.size[0], img.size[1])
+        # iprev.image_pixels_float = img.pixels[:]
+
     icon_collections["main"] = pcoll
 
 
diff --git a/blenderkit/search.py b/blenderkit/search.py
index 880884ecd03a652b467e2b6dd796e04774895550..2252d3f1521efe88c9fbf41d6c248bf848b13036 100644
--- a/blenderkit/search.py
+++ b/blenderkit/search.py
@@ -16,8 +16,6 @@
 #
 # ##### END GPL LICENSE BLOCK #####
 
-
-
 from blenderkit import paths, utils, categories, ui, colors, bkit_oauth, version_checker, tasks_queue, rerequests, \
     resolutions
 
@@ -51,6 +49,7 @@ import json
 import math
 
 import logging
+
 bk_logger = logging.getLogger('blenderkit')
 
 search_start_time = 0
@@ -320,6 +319,7 @@ def timer_update():
     global first_time
     preferences = bpy.context.preferences.addons['blenderkit'].preferences
     if first_time and not bpy.app.background:  # first time
+
         first_time = False
         if preferences.show_on_start:
             # TODO here it should check if there are some results, and only open assetbar if this is the case, not search.
@@ -437,11 +437,14 @@ def load_previews():
             if not r['thumbnail_small']:
                 tpath = paths.get_addon_thumbnail_path('thumbnail_not_available.jpg')
 
+            if not os.path.exists(tpath):
+                continue
             iname = utils.previmg_name(i)
 
             # if os.path.exists(tpath):  # sometimes we are unlucky...
             img = bpy.data.images.get(iname)
-            if img is None and os.path.exists(tpath):
+
+            if img is None:
                 img = bpy.data.images.load(tpath)
                 img.name = iname
             elif img.filepath != tpath:
@@ -897,7 +900,7 @@ class Searcher(threading.Thread):
         # result ordering: _score - relevance, score - BlenderKit score
         order = []
         if params['free_first']:
-            order = ['-is_free',]
+            order = ['-is_free', ]
         if query.get('query') is None and query.get('category_subtree') == None:
             # assumes no keywords and no category, thus an empty search that is triggered on start.
             # orders by last core file upload
diff --git a/blenderkit/ui.py b/blenderkit/ui.py
index 9f60d471fc696a51b3561c51069705107402bb68..0dfed1d6ccc131782a647201f23021fc52c47e8d 100644
--- a/blenderkit/ui.py
+++ b/blenderkit/ui.py
@@ -21,6 +21,7 @@
 from blenderkit import paths, ratings, utils, search, upload, ui_bgl, download, bg_blender, colors, tasks_queue, \
     ui_panels,icons
 
+
 import bpy
 
 import math, random
@@ -304,7 +305,6 @@ def draw_tooltip(x, y, text='', author='', img=None, gravatar=None):
     isizey = int(512 * scale * img.size[1] / max(img.size[0], img.size[1]))
 
     estimated_height = 2 * ttipmargin + textmargin + isizey
-
     if estimated_height > y:
         scaledown = y / (estimated_height)
         scale *= scaledown
@@ -350,7 +350,6 @@ def draw_tooltip(x, y, text='', author='', img=None, gravatar=None):
                      bgcol)
     # main preview image
     ui_bgl.draw_image(x, y - isizey - ttipmargin, isizex, isizey, img, 1)
-
     # text overlay background
     ui_bgl.draw_rect(x - ttipmargin,
                      y - 2 * ttipmargin - isizey,
@@ -436,6 +435,23 @@ def draw_tooltip(x, y, text='', author='', img=None, gravatar=None):
 
     t = time.time()
 
+def draw_tooltip_with_author(asset_data, x,y):
+     # TODO move this lazy loading into a function and don't duplicate through the code
+
+    img = get_large_thumbnail_image(asset_data)
+    gimg = None
+    atip = ''
+    if bpy.context.window_manager.get('bkit authors') is not None:
+        a = bpy.context.window_manager['bkit authors'].get(asset_data['author']['id'])
+        if a is not None and a != '':
+            if a.get('gravatarImg') is not None:
+                gimg = utils.get_hidden_image(a['gravatarImg'], a['gravatarHash'])
+            atip = a['tooltip']
+
+    # scene = bpy.context.scene
+    # ui_props = scene.blenderkitUI
+    draw_tooltip(x,y, text=asset_data['tooltip'], author=atip, img=img,
+                 gravatar=gimg)
 
 def draw_tooltip_old(x, y, text='', author='', img=None):
     region = bpy.context.region
@@ -678,6 +694,22 @@ def is_upload_old(asset_data):
         return (age.days - old.days)
     return 0
 
+def get_large_thumbnail_image(asset_data):
+    '''Get thumbnail image from asset data'''
+    scene = bpy.context.scene
+    ui_props = scene.blenderkitUI
+    iname = utils.previmg_name(ui_props.active_index, fullsize=True)
+    directory = paths.get_temp_dir('%s_search' % mappingdict[ui_props.asset_type])
+    tpath = os.path.join(directory, asset_data['thumbnail'])
+    if not asset_data['thumbnail']:
+        tpath = paths.get_addon_thumbnail_path('thumbnail_not_available.jpg')
+
+    if asset_data['assetType'] == 'hdr':
+        colorspace = 'Non-Color'
+    else:
+        colorspace = 'sRGB'
+    img = utils.get_hidden_image(tpath, iname, colorspace=colorspace)
+    return img
 
 def draw_asset_bar(self, context):
     s = bpy.context.scene
@@ -817,63 +849,17 @@ def draw_asset_bar(self, context):
             #     report = 'BlenderKit - No matching results found.'
             #     ui_bgl.draw_text(report, ui_props.bar_x + ui_props.margin,
             #                      ui_props.bar_y - 25 - ui_props.margin, 15)
+            if ui_props.draw_tooltip:
+                r = search_results[ui_props.active_index]
+                draw_tooltip_with_author(r, ui_props.mouse_x, ui_props.mouse_y)
         s = bpy.context.scene
         props = utils.get_search_props()
         # if props.report != '' and props.is_searching or props.search_error:
         #     ui_bgl.draw_text(props.report, ui_props.bar_x,
         #                      ui_props.bar_y - 15 - ui_props.margin - ui_props.bar_height, 15)
 
-        props = s.blenderkitUI
-        if props.draw_tooltip:
-            # TODO move this lazy loading into a function and don't duplicate through the code
-            iname = utils.previmg_name(ui_props.active_index, fullsize=True)
-
-            directory = paths.get_temp_dir('%s_search' % mappingdict[props.asset_type])
-            sr = s.get('search results')
-            if sr != None and -1 < ui_props.active_index < len(sr):
-                r = sr[ui_props.active_index]
-                tpath = os.path.join(directory, r['thumbnail'])
-                if not r['thumbnail']:
-                    tpath = paths.get_addon_thumbnail_path('thumbnail_not_available.jpg')
-
-                # img = bpy.data.images.get(iname)
-                # if img == None or img.filepath != tpath:
-                #     # TODO replace it with a function
-                #     if os.path.exists(tpath):
-                #
-                #         if img is None:
-                #             img = bpy.data.images.load(tpath)
-                #             img.name = iname
-                #         else:
-                #             if img.filepath != tpath:
-                #                 # todo replace imgs reloads with a method that forces unpack for thumbs.
-                #                 if img.packed_file is not None:
-                #                     img.unpack(method='USE_ORIGINAL')
-                #                 img.filepath = tpath
-                #                 img.reload()
-                #                 img.name = iname
-                #     else:
-                #         iname = utils.previmg_name(ui_props.active_index)
-                #         img = bpy.data.images.get(iname)
-                #     if img:
-                #         img.colorspace_settings.name = 'sRGB'
-                if r['assetType'] == 'hdr':
-                    colorspace = 'Non-Color'
-                else:
-                    colorspace = 'sRGB'
-                img = utils.get_hidden_image(tpath, iname, colorspace=colorspace)
 
-                gimg = None
-                atip = ''
-                if bpy.context.window_manager.get('bkit authors') is not None:
-                    a = bpy.context.window_manager['bkit authors'].get(r['author']['id'])
-                    if a is not None and a != '':
-                        if a.get('gravatarImg') is not None:
-                            gimg = utils.get_hidden_image(a['gravatarImg'], a['gravatarHash'])
-                        atip = a['tooltip']
 
-                draw_tooltip(ui_props.mouse_x, ui_props.mouse_y, text=ui_props.tooltip, author=atip, img=img,
-                             gravatar=gimg)
 
     if ui_props.dragging and (
             ui_props.draw_drag_image or ui_props.draw_snapped_bounds) and ui_props.active_index > -1:
@@ -1303,9 +1289,6 @@ class AssetBarOperator(bpy.types.Operator):
             ui_props.mouse_x = 0
             ui_props.mouse_y = self.region.height
 
-            mx = event.mouse_x
-            my = event.mouse_y
-
             ui_props.draw_tooltip = True
 
             # only generate tooltip once in a while
@@ -1472,6 +1455,7 @@ class AssetBarOperator(bpy.types.Operator):
             my = event.mouse_y - r.y
 
             if event.value == 'PRESS' and mouse_in_asset_bar(mx, my):
+                # bpy.ops.wm.blenderkit_asset_popup('INVOKE_DEFAULT')
                 bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu')
                 return {'RUNNING_MODAL'}
 
@@ -1803,6 +1787,205 @@ class UndoWithContext(bpy.types.Operator):
         return {'FINISHED'}
 
 
+def draw_callback_dragging(self, context):
+    img = bpy.data.images.get(self.iname)
+    linelength = 35
+    scene = bpy.context.scene
+    ui_props = scene.blenderkitUI
+    ui_bgl.draw_image(self.mouse_x + linelength, self.mouse_y - linelength - ui_props.thumb_size,
+                      ui_props.thumb_size, ui_props.thumb_size, img, 1)
+    ui_bgl.draw_line2d(self.mouse_x, self.mouse_y, self.mouse_x + linelength,
+                       self.mouse_y - linelength, 2, colors.WHITE)
+
+
+def draw_callback_3d_dragging(self, context):
+    ''' Draw snapped bbox while dragging. '''
+    if not utils.guard_from_crash():
+        return
+    ui_props = context.scene.blenderkitUI
+    # print(ui_props.asset_type, self.has_hit, self.snapped_location)
+    if ui_props.asset_type == 'MODEL':
+        if self.has_hit:
+            draw_bbox(self.snapped_location, self.snapped_rotation, self.snapped_bbox_min, self.snapped_bbox_max)
+
+
+class AssetDragOperator(bpy.types.Operator):
+    """Draw a line with the mouse"""
+    bl_idname = "view3d.asset_drag_drop"
+    bl_label = "BlenderKit asset drag drop"
+
+    asset_search_index: IntProperty(name="Active Index", default=0)
+
+    def handlers_remove(self):
+        bpy.types.SpaceView3D.draw_handler_remove(self._handle, 'WINDOW')
+        bpy.types.SpaceView3D.draw_handler_remove(self._handle_3d, 'WINDOW')
+
+    def mouse_release(self):
+        scene = bpy.context.scene
+        ui_props = scene.blenderkitUI
+
+        if not self.has_hit:
+            return {'RUNNING_MODAL'}
+
+        if ui_props.asset_type == 'MODEL':
+            target_object = ''
+            if self.object_name is not None:
+                target_object = self.object_name
+                target_slot = ''
+
+        if ui_props.asset_type == 'MATERIAL':
+            # first, test if object can have material applied.
+            object = bpy.data.objects[self.object_name]
+            if object is not None and not object.is_library_indirect and object.type == 'MESH':
+                target_object = object.name
+                # create final mesh to extract correct material slot
+                depsgraph = bpy.context.evaluated_depsgraph_get()
+                object_eval = object.evaluated_get(depsgraph)
+                temp_mesh = object_eval.to_mesh()
+                target_slot = temp_mesh.polygons[self.face_index].material_index
+                object_eval.to_mesh_clear()
+            else:
+                self.report({'WARNING'}, "Invalid or library object as input:")
+                target_object = ''
+                target_slot = ''
+
+        if abs(self.start_mouse_x - self.mouse_x) < 20 and abs(self.start_mouse_y - self.mouse_y)<20:
+            #no dragging actually this was a click.
+            self.snapped_location = scene.cursor.location
+            self.snapped_rotation = (0,0,0)
+            if ui_props.asset_type in ('MATERIAL',):
+                ao = bpy.context.active_object
+                if ao != None and not ao.is_library_indirect:
+                    target_object = bpy.context.active_object.name
+                    target_slot = bpy.context.active_object.active_material_index
+                    # change snapped location for placing material downloader.
+                    self.snapped_location = bpy.context.active_object.location
+                else:
+                    target_object = ''
+                    target_slot = ''
+
+
+        # picking of assets and using them
+        if ui_props.asset_type == 'MATERIAL':
+            if target_object != '':
+                # position is for downloader:
+                loc = self.snapped_location
+                rotation = (0, 0, 0)
+
+                utils.automap(target_object, target_slot=target_slot,
+                              tex_size=self.asset_data.get('texture_size_meters', 1.0))
+                bpy.ops.scene.blenderkit_download(True,
+                                                  # asset_type=ui_props.asset_type,
+                                                  asset_index=self.asset_search_index,
+                                                  model_location=loc,
+                                                  model_rotation=rotation,
+                                                  target_object=target_object,
+                                                  material_target_slot=target_slot)
+
+
+        elif ui_props.asset_type == 'MODEL':
+
+            if 'particle_plants' in self.asset_data['tags']:
+                bpy.ops.object.blenderkit_particles_drop("INVOKE_DEFAULT",
+                                                         asset_search_index=self.asset_search_index,
+                                                         model_location=self.snapped_location,
+                                                         model_rotation=self.snapped_rotation,
+                                                         target_object=target_object)
+            else:
+                bpy.ops.scene.blenderkit_download(True,
+                                                  # asset_type=ui_props.asset_type,
+                                                  asset_index=self.asset_search_index,
+                                                  model_location=self.snapped_location,
+                                                  model_rotation=self.snapped_rotation,
+                                                  target_object=target_object)
+
+        else:
+            bpy.ops.scene.blenderkit_download(  # asset_type=ui_props.asset_type,
+                asset_index=self.asset_search_index)
+
+    def modal(self, context, event):
+        scene = bpy.context.scene
+        ui_props = scene.blenderkitUI
+        context.area.tag_redraw()
+
+        # if event.type == 'MOUSEMOVE':
+        if not hasattr(self,'start_mouse_x'):
+            self.start_mouse_x = event.mouse_region_x
+            self.start_mouse_y = event.mouse_region_y
+
+        self.mouse_x = event.mouse_region_x
+        self.mouse_y = event.mouse_region_y
+
+        if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
+            self.mouse_release()
+            self.handlers_remove()
+            return {'FINISHED'}
+
+        elif event.type in {'RIGHTMOUSE', 'ESC'}:
+            self.handlers_remove()
+            return {'CANCELLED'}
+
+        sprops = bpy.context.scene.blenderkit_models
+        if event.type == 'WHEELUPMOUSE':
+            sprops.offset_rotation_amount += sprops.offset_rotation_step
+        elif event.type == 'WHEELDOWNMOUSE':
+            sprops.offset_rotation_amount -= sprops.offset_rotation_step
+
+        #### TODO - this snapping code below is 3x in this file.... refactor it.
+        self.has_hit, self.snapped_location, self.snapped_normal, self.snapped_rotation, self.face_index, object, self.matrix = mouse_raycast(
+            context, event.mouse_region_x, event.mouse_region_y)
+        if object is not None:
+            self.object_name  =object.name
+
+
+
+        # MODELS can be dragged on scene floor
+        if not self.has_hit and ui_props.asset_type == 'MODEL':
+            self.has_hit, self.snapped_location, self.snapped_normal, self.snapped_rotation, self.face_index, object, self.matrix = floor_raycast(
+                context,
+                event.mouse_region_x, event.mouse_region_y)
+            if object is not None:
+                self.object_name = object.name
+
+        if ui_props.asset_type == 'MODEL':
+            self.snapped_bbox_min = Vector(self.asset_data['bbox_min'])
+            self.snapped_bbox_max = Vector(self.asset_data['bbox_max'])
+
+        return {'RUNNING_MODAL'}
+
+    def invoke(self, context, event):
+        if context.area.type == 'VIEW_3D':
+            # the arguments we pass the the callback
+            args = (self, context)
+            # Add the region OpenGL drawing callback
+            # draw in view space with 'POST_VIEW' and 'PRE_VIEW'
+            self.iname = utils.previmg_name(self.asset_search_index)
+
+            self._handle = bpy.types.SpaceView3D.draw_handler_add(draw_callback_dragging, args, 'WINDOW', 'POST_PIXEL')
+            self._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d_dragging, args, 'WINDOW',
+                                                                     'POST_VIEW')
+
+            self.mouse_x = 0
+            self.mouse_y = 0
+
+            self.has_hit = False
+            self.snapped_location = (0,0,0)
+            self.snapped_normal = (0,0,1)
+            self.snapped_rotation = (0,0,0)
+            self.face_index = 0
+            object = None
+            self.matrix = None
+
+            sr = bpy.context.scene['search results']
+            self.asset_data = sr[self.asset_search_index]
+
+            context.window_manager.modal_handler_add(self)
+            return {'RUNNING_MODAL'}
+        else:
+            self.report({'WARNING'}, "View3D not found, cannot run operator")
+            return {'CANCELLED'}
+
+
 class RunAssetBarWithContext(bpy.types.Operator):
     """Regenerate cobweb"""
     bl_idname = "object.run_assetbar_fix_context"
@@ -1816,13 +1999,19 @@ class RunAssetBarWithContext(bpy.types.Operator):
     def execute(self, context):
         C_dict = utils.get_fake_context(context)
         if C_dict.get('window'):  # no 3d view, no asset bar.
-            bpy.ops.view3d.blenderkit_asset_bar(C_dict, 'INVOKE_REGION_WIN', keep_running=True, do_search=False)
+            preferences = bpy.context.preferences.addons['blenderkit'].preferences
+            if preferences.experimental_features:
+                bpy.ops.view3d.blenderkit_asset_bar_widget(C_dict, 'INVOKE_REGION_WIN', keep_running=True, do_search=False)
+
+            else:
+                bpy.ops.view3d.blenderkit_asset_bar(C_dict, 'INVOKE_REGION_WIN', keep_running=True, do_search=False)
         return {'FINISHED'}
 
 
 classes = (
     AssetBarOperator,
     # AssetBarExperiment,
+    AssetDragOperator,
     RunAssetBarWithContext,
     TransferBlenderkitData,
     UndoWithContext,
diff --git a/blenderkit/ui_panels.py b/blenderkit/ui_panels.py
index b194568c448ce984325a470c32c6f23f1f725742..4948b65165bc924fbe2adf1f72ae4234a123c766 100644
--- a/blenderkit/ui_panels.py
+++ b/blenderkit/ui_panels.py
@@ -17,7 +17,7 @@
 # ##### END GPL LICENSE BLOCK #####
 
 
-from blenderkit import paths, ratings, utils, download, categories, icons, search, resolutions
+from blenderkit import paths, ratings, utils, download, categories, icons, search, resolutions, ui
 
 from bpy.types import (
     Panel
@@ -313,11 +313,19 @@ def draw_assetbar_show_hide(layout, props):
     else:
         icon = 'HIDE_ON'
         ttip = 'Click to Show Asset Bar'
-    op = layout.operator('view3d.blenderkit_asset_bar', text='', icon=icon)
-    op.keep_running = False
-    op.do_search = False
 
-    op.tooltip = ttip
+    preferences = bpy.context.preferences.addons['blenderkit'].preferences
+    if preferences.experimental_features:
+        op = layout.operator('view3d.blenderkit_asset_bar_widget', text = '', icon = icon)
+        op.keep_running = False
+        op.do_search = False
+        op.tooltip = ttip
+    else:
+        op = layout.operator('view3d.blenderkit_asset_bar', text='', icon=icon)
+        op.keep_running = False
+        op.do_search = False
+
+        op.tooltip = ttip
 
 
 def draw_panel_model_search(self, context):
@@ -403,7 +411,7 @@ class VIEW3D_PT_blenderkit_model_properties(Panel):
             draw_panel_model_rating(self, context)
 
             layout.label(text='Asset tools:')
-            draw_asset_context_menu(self, context, ad, from_panel=True)
+            draw_asset_context_menu(self.layout, context, ad, from_panel=True)
             # if 'rig' in ad['tags']:
             #     # layout.label(text = 'can make proxy')
             #     layout.operator('object.blenderkit_make_proxy', text = 'Make Armature proxy')
@@ -447,7 +455,7 @@ class NODE_PT_blenderkit_material_properties(Panel):
             draw_panel_material_ratings(self, context)
 
             layout.label(text='Asset tools:')
-            draw_asset_context_menu(self, context, ad, from_panel=True)
+            draw_asset_context_menu(self.layout, context, ad, from_panel=True)
             # if 'rig' in ad['tags']:
             #     # layout.label(text = 'can make proxy')
             #     layout.operator('object.blenderkit_make_proxy', text = 'Make Armature proxy')
@@ -934,7 +942,6 @@ class VIEW3D_PT_blenderkit_unified(Panel):
         user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
         wm = bpy.context.window_manager
         layout = self.layout
-
         # layout.prop_tabs_enum(ui_props, "asset_type", icon_only = True)
 
         row = layout.row()
@@ -1122,8 +1129,7 @@ class BlenderKitWelcomeOperator(bpy.types.Operator):
         return wm.invoke_props_dialog(self)
 
 
-def draw_asset_context_menu(self, context, asset_data, from_panel=False):
-    layout = self.layout
+def draw_asset_context_menu(layout, context, asset_data, from_panel=False):
     ui_props = context.scene.blenderkitUI
 
     author_id = str(asset_data['author'].get('id'))
@@ -1269,7 +1275,7 @@ def draw_asset_context_menu(self, context, asset_data, from_panel=False):
                 op.asset_id = asset_data['id']
                 op.state = 'rejected'
 
-        if author_id == str(profile['user']['id']):
+        if author_id == str(profile['user']['id']) or utils.profile_is_validator():
             layout.label(text='Management tools:')
 
             row = layout.row()
@@ -1277,6 +1283,7 @@ def draw_asset_context_menu(self, context, asset_data, from_panel=False):
             op = layout.operator('wm.blenderkit_fast_metadata', text='Fast Edit Metadata')
             op.asset_id = asset_data['id']
 
+        if author_id == str(profile['user']['id']):
             row = layout.row()
             row.operator_context = 'INVOKE_DEFAULT'
             op = row.operator('object.blenderkit_change_status', text='Delete')
@@ -1342,12 +1349,101 @@ class OBJECT_MT_blenderkit_asset_menu(bpy.types.Menu):
     def draw(self, context):
         ui_props = context.scene.blenderkitUI
 
+        sr = bpy.context.scene['search results']
+        asset_data = sr[ui_props.active_index]
+        draw_asset_context_menu(self.layout, context, asset_data, from_panel=False)
+
+        # ui_props = context.scene.blenderkitUI
+        #
         # sr = bpy.context.scene['search results']
+        # asset_data = sr[ui_props.active_index]
+        # layout = self.layout
+        # row = layout.row()
+        # split = row.split(factor=0.2)
+        # col = split.column()
+        # op = col.operator('view3d.asset_drag_drop')
+        # op.asset_search_index=ui_props.active_index
+        #
+        # draw_asset_context_menu(col, context, asset_data, from_panel=False)
+        # split = split.split(factor=0.3)
+        # col1 = split.column()
+        # box = col1.box()
+        # utils.label_multiline(box, asset_data['tooltip'])
+        # col2 = split.column()
+        #
+        # pcoll = icons.icon_collections["main"]
+        # my_icon = pcoll['test']
+        # row = col2.row()
+        # row.scale_y = 4
+        # row.template_icon(icon_value=my_icon.icon_id, scale=2.0)
+        # # col2.template_icon(icon_value=self.img.preview.icon_id, scale=10.0)
+        # box2 = col2.box()
+        #
+        # box2.label(text='and heere goes the rating')
+        # box2.label(text='************')
+        # box2.label(text='dadydadadada')
+
+class AssetPopupCard(bpy.types.Operator):
+    """Generate Cycles thumbnail for model assets"""
+    bl_idname = "wm.blenderkit_asset_popup"
+    bl_label = "BlenderKit asset popup"
+    # bl_options = {'REGISTER', 'INTERNAL'}
+    bl_options = {'REGISTER',}
+
+    @classmethod
+    def poll(cls, context):
+        return True
+
+    def draw(self, context):
+        ui_props = context.scene.blenderkitUI
+
         sr = bpy.context.scene['search results']
         asset_data = sr[ui_props.active_index]
+        layout = self.layout
+        row = layout.row()
+        split = row.split(factor=0.2)
+        col = split.column()
+        op = col.operator('view3d.asset_drag_drop')
+        op.asset_search_index = ui_props.active_index
+        draw_asset_context_menu(col, context, asset_data, from_panel=False)
+        split = split.split(factor=0.5)
+        col1 = split.column()
+        box = col1.box()
+        utils.label_multiline(box,asset_data['tooltip'], width = 300)
+
+        col2 = split.column()
+
+
+        pcoll = icons.icon_collections["main"]
+        my_icon = pcoll['test']
+        col2.template_icon(icon_value=my_icon.icon_id, scale=20.0)
+        # col2.template_icon(icon_value=self.img.preview.icon_id, scale=10.0)
+        box2 = col2.box()
+
+        # draw_ratings(box2, context, asset_data)
+        box2.label(text = 'Ratings')
+        # print(tp, dir(tp))
+        # if not hasattr(self, 'first_draw'):# try to redraw because of template preview which needs update
+        #     for region in context.area.regions:
+        #         region.tag_redraw()
+        #     self.first_draw = True
+
+    def execute(self, context):
+        print('execute')
+        return {'FINISHED'}
 
-        draw_asset_context_menu(self, context, asset_data, from_panel=False)
+    def invoke(self, context, event):
+        wm = context.window_manager
+        ui_props = context.scene.blenderkitUI
+        ui_props.draw_tooltip = False
+        sr = bpy.context.scene['search results']
+        asset_data = sr[ui_props.active_index]
+        self.img = ui.get_large_thumbnail_image(asset_data)
+        # self.tex = utils.get_hidden_texture(self.img)
+        # self.tex.update_tag()
 
+        bl_label  = asset_data['name']
+        return wm.invoke_props_dialog(self, width = 700)
 
 class OBJECT_MT_blenderkit_login_menu(bpy.types.Menu):
     bl_label = "BlenderKit login/signup:"
@@ -1435,8 +1531,8 @@ class UrlPopupDialog(bpy.types.Operator):
 
 
 class LoginPopupDialog(bpy.types.Operator):
-    """Generate Cycles thumbnail for model assets"""
-    bl_idname = "wm.blenderkit_url_dialog"
+    """Popup a dialog which enables the user to log in after being logged out automatically."""
+    bl_idname = "wm.blenderkit_login_dialog"
     bl_label = "BlenderKit login"
     bl_options = {'REGISTER', 'INTERNAL'}
 
@@ -1502,10 +1598,15 @@ def draw_panel_categories(self, context):
                 row = row.split(factor=.8, align=True)
             # row = split.split()
             ctext = '%s (%i)' % (c['name'], c['assetCount'])
-            op = row.operator('view3d.blenderkit_asset_bar', text=ctext)
-            op.do_search = True
-            op.keep_running = True
-            op.category = c['slug']
+
+            preferences = bpy.context.preferences.addons['blenderkit'].preferences
+            if preferences.experimental_features:
+                op = row.operator('view3d.blenderkit_asset_bar_widget', text=ctext)
+            else:
+                op = row.operator('view3d.blenderkit_asset_bar', text=ctext)
+                op.do_search = True
+                op.keep_running = True
+                op.category = c['slug']
             # TODO enable subcategories, now not working due to some bug on server probably
             if len(c['children']) > 0 and c['assetCount'] > 15:
                 # row = row.split()
@@ -1602,6 +1703,7 @@ classes = (
     # OBJECT_MT_blenderkit_resolution_menu,
     OBJECT_MT_blenderkit_asset_menu,
     OBJECT_MT_blenderkit_login_menu,
+    AssetPopupCard,
     UrlPopupDialog,
     BlenderKitWelcomeOperator,
 )
diff --git a/blenderkit/utils.py b/blenderkit/utils.py
index 38e345510cc8d938bfaa59a654cbff8d5d5f4dc9..fde5b0025add3b7fb280fa4d304d8cc5d9956371 100644
--- a/blenderkit/utils.py
+++ b/blenderkit/utils.py
@@ -39,6 +39,10 @@ NORMAL_PRIORITY_CLASS = 0x00000020
 REALTIME_PRIORITY_CLASS = 0x00000100
 
 
+def experimental_enabled():
+    preferences = bpy.context.preferences.addons['blenderkit'].preferences
+    return preferences.experimental_features
+
 def get_process_flags():
     flags = BELOW_NORMAL_PRIORITY_CLASS
     if sys.platform != 'win32':  # TODO test this on windows
@@ -299,14 +303,14 @@ def uploadable_asset_poll():
         return ui_props.hdr_upload_image is not None
     return True
 
-def get_hidden_texture(tpath, bdata_name, force_reload=False):
-    i = get_hidden_image(tpath, bdata_name, force_reload=force_reload)
-    bdata_name = f".{bdata_name}"
-    t = bpy.data.textures.get(bdata_name)
+def get_hidden_texture(img, force_reload=False):
+    # i = get_hidden_image(tpath, bdata_name, force_reload=force_reload)
+    # bdata_name = f".{bdata_name}"
+    t = bpy.data.textures.get(img.name)
     if t is None:
-        t = bpy.data.textures.new('.test', 'IMAGE')
-    if t.image != i:
-        t.image = i
+        t = bpy.data.textures.new(img.name, 'IMAGE')
+    if t.image != img:
+        t.image = img
     return t