Skip to content
Snippets Groups Projects
ui.py 76.5 KiB
Newer Older
  • Learn to ignore specific revisions
  • Vilem Duha's avatar
    Vilem Duha committed
    
    
    def is_rating_possible():
        ao = bpy.context.active_object
        ui = bpy.context.scene.blenderkitUI
    
    Vilém Duha's avatar
    Vilém Duha committed
        preferences = bpy.context.preferences.addons['blenderkit'].preferences
        #first test if user is logged in.
        if preferences.api_key == '':
            return False, False, None, None
    
    Vilem Duha's avatar
    Vilem Duha committed
        if bpy.context.scene.get('assets rated') is not None and ui.down_up == 'SEARCH':
            if bpy.context.mode in ('SCULPT', 'PAINT_TEXTURE'):
                b = utils.get_active_brush()
                ad = b.get('asset_data')
                if ad is not None:
    
                    rated = bpy.context.scene['assets rated'].get(ad['assetBaseId'])
    
    Vilem Duha's avatar
    Vilem Duha committed
                    return True, rated, b, ad
            if ao is not None:
    
                # crawl parents to reach active asset. there could have been parenting so we need to find the first onw
    
                ao_check = ao
                while ad is None or (ad is None and ao_check.parent is not None):
    
    Vilém Duha's avatar
    Vilém Duha committed
                    s = bpy.context.scene
    
                    ad = ao_check.get('asset_data')
                    if ad is not None:
    
    Vilém Duha's avatar
    Vilém Duha committed
    
                        s['assets rated'] = s.get('assets rated',{})
    
                        rated = s['assets rated'].get(ad.get('assetBaseId'))
    
                        # originally hidden for already rated assets
                        return True, rated, ao_check, ad
                    elif ao_check.parent is not None:
                        ao_check = ao_check.parent
                    else:
                        break;
    
    
    Vilem Duha's avatar
    Vilem Duha committed
                # check also materials
                m = ao.active_material
                if m is not None:
                    ad = m.get('asset_data')
    
                    if ad is not None and ad.get('assetBaseId'):
    
                        rated = bpy.context.scene['assets rated'].get(ad['assetBaseId'])
    
                        if rated:
                            return True, rated, m, ad
    
    Vilem Duha's avatar
    Vilem Duha committed
    
            # if t>2 and t<2.5:
            #     ui_props.rating_on = False
    
        return False, False, None, None
    
    
    def interact_rating(r, mx, my, event):
        ui = bpy.context.scene.blenderkitUI
        rating_possible, rated, asset, asset_data = is_rating_possible()
        if rating_possible:
            bkit_ratings = asset.bkit_ratings
    
            t = time.time() - ui.last_rating_time
    
            if bpy.context.mode in ('SCULPT', 'PAINT_TEXTURE'):
                accept_value = 'PRESS'
            else:
                accept_value = 'RELEASE'
    
            if ui.rating_button_on and event.type == 'LEFTMOUSE' and event.value == accept_value:
    
    Vilem Duha's avatar
    Vilem Duha committed
                if mouse_in_area(mx, my,
                                 ui.rating_x,
                                 ui.rating_y - ui.rating_button_width,
                                 ui.rating_button_width * 2,
                                 ui.rating_button_width):
                    ui.rating_menu_on = True
                    ui.rating_button_on = False
                    return True
            if ui.rating_menu_on:
                if mouse_in_area(mx, my,
                                 ui.rating_x,
                                 ui.rating_y - ui.rating_ui_height,
                                 ui.rating_ui_width,
                                 ui.rating_ui_height + 25):
                    rmx = mx - (ui.rating_x)
                    rmy = my - (ui.rating_y - ui.rating_ui_height)
    
                    # quality
                    upload_rating = False
                    if (ui.quality_stars_x < rmx and rmx < ui.quality_stars_x + 10 * ui.star_size and \
                        ui.quality_stars_y < rmy and rmy < ui.quality_stars_y + ui.star_size and event.type == 'LEFTMOUSE' and event.value == 'PRESS') or \
                            ui.dragging_rating_quality:
    
                        if event.type == 'LEFTMOUSE':
                            if event.value == 'PRESS':
                                ui.dragging_rating = True
                                ui.dragging_rating_quality = True
                            elif event.value == 'RELEASE':
                                ui.dragging_rating = False
                                ui.dragging_rating_quality = False
    
                        if ui.dragging_rating_quality:
                            q = math.ceil((rmx - ui.quality_stars_x) / (float(ui.star_size)))
                            bkit_ratings.rating_quality = q
    
                    # work hours
                    if (
                            ui.workhours_bar_x < rmx and rmx < ui.workhours_bar_x + ui.workhours_bar_x_max + ui.workhours_bar_slider_size and \
                            ui.workhours_bar_y < rmy and rmy < ui.workhours_bar_y + ui.workhours_bar_slider_size and event.type == 'LEFTMOUSE' and event.value == 'PRESS') \
                            or (ui.dragging_rating_work_hours):
                        if event.value == 'PRESS':
                            ui.dragging_rating = True
                            ui.dragging_rating_work_hours = True
                        elif event.value == 'RELEASE':
                            ui.dragging_rating = False
                            ui.dragging_rating_work_hours = False
                        if ui.dragging_rating_work_hours:
                            xv = rmx - ui.workhours_bar_x - ui.workhours_bar_slider_size / 2
                            ratio = xv / ui.workhours_bar_x_max
    
                            if asset_data['assetType'] == 'model':
    
    Vilem Duha's avatar
    Vilem Duha committed
                                wh_log2 = ratio * 9 - 1
                                wh = 2 ** wh_log2
                            else:
                                wh = 5 * ratio
                            bkit_ratings.rating_work_hours = wh
    
                    if event.type == 'LEFTMOUSE' and event.value == 'RELEASE':
    
    Vilém Duha's avatar
    Vilém Duha committed
                        ui.last_rating_time = time.time() # this prop seems obsolete now?
    
    Vilem Duha's avatar
    Vilem Duha committed
                    return True
                else:
                    ui.rating_button_on = True
                    ui.rating_menu_on = False
        return False
    
    
    def mouse_in_area(mx, my, x, y, w, h):
        if x < mx < x + w and y < my < y + h:
            return True
        else:
            return False
    
    
    def mouse_in_asset_bar(mx, my):
    
    Vilem Duha's avatar
    Vilem Duha committed
        ui_props = bpy.context.scene.blenderkitUI
    
        # search_results = s.get('search results')
        # if search_results == None:
        #     return False
        #
        # w_draw1 = min(ui_props.wcount + 1, len(search_results) - b * ui_props.wcount - ui_props.scrolloffset)
        # end = ui_props.bar_x + (w_draw1) * (
        #         ui_props.margin + ui_props.thumb_size) + ui_props.margin + ui_props.drawoffset + 25
    
    Vilem Duha's avatar
    Vilem Duha committed
        if ui_props.bar_y - ui_props.bar_height < my < ui_props.bar_y \
    
                and mx > ui_props.bar_x and mx < ui_props.bar_x + ui_props.bar_width:
    
    Vilem Duha's avatar
    Vilem Duha committed
            return True
        else:
            return False
    
    
    def mouse_in_region(r, mx, my):
        if 0 < my < r.height and 0 < mx < r.width:
            return True
        else:
            return False
    
    
    def update_ui_size(area, region):
        ui = bpy.context.scene.blenderkitUI
        user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
        ui_scale = bpy.context.preferences.view.ui_scale
    
        ui.margin = ui.bl_rna.properties['margin'].default * ui_scale
    
        ui.thumb_size = user_preferences.thumb_size * ui_scale
    
    Vilem Duha's avatar
    Vilem Duha committed
    
        reg_multiplier = 1
        if not bpy.context.preferences.system.use_region_overlap:
            reg_multiplier = 0
    
        for r in area.regions:
            if r.type == 'TOOLS':
                ui.bar_x = r.width * reg_multiplier + ui.margin + ui.bar_x_offset * ui_scale
            elif r.type == 'UI':
                ui.bar_end = r.width * reg_multiplier + 100 * ui_scale
    
        ui.bar_width = region.width - ui.bar_x - ui.bar_end
        ui.wcount = math.floor(
            (ui.bar_width - 2 * ui.drawoffset) / (ui.thumb_size + ui.margin))
    
        search_results = bpy.context.scene.get('search results')
    
        if search_results != None and ui.wcount > 0:
    
    Vilem Duha's avatar
    Vilem Duha committed
            ui.hcount = min(user_preferences.max_assetbar_rows, math.ceil(len(search_results) / ui.wcount))
        else:
            ui.hcount = 1
        ui.bar_height = (ui.thumb_size + ui.margin) * ui.hcount + ui.margin
        ui.bar_y = region.height - ui.bar_y_offset * ui_scale
        if ui.down_up == 'UPLOAD':
    
            ui.reports_y = ui.bar_y - 600
    
    Vilem Duha's avatar
    Vilem Duha committed
            ui.reports_x = ui.bar_x
        else:
    
            ui.reports_y = ui.bar_y - ui.bar_height - 100
    
    Vilem Duha's avatar
    Vilem Duha committed
            ui.reports_x = ui.bar_x
    
        ui.rating_x = ui.bar_x
        ui.rating_y = ui.bar_y - ui.bar_height
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    class AssetBarOperator(bpy.types.Operator):
        '''runs search and displays the asset bar at the same time'''
        bl_idname = "view3d.blenderkit_asset_bar"
        bl_label = "BlenderKit Asset Bar UI"
    
    Vilém Duha's avatar
    Vilém Duha committed
        bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
    
    Vilem Duha's avatar
    Vilem Duha committed
    
        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 Only", 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
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        def search_more(self):
    
    Vilem Duha's avatar
    Vilem Duha committed
            sro = bpy.context.scene.get('search results orig')
            if sro is not None and sro.get('next') is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                search.search(get_next=True)
    
        def exit_modal(self):
            try:
                bpy.types.SpaceView3D.draw_handler_remove(self._handle_2d, 'WINDOW')
                bpy.types.SpaceView3D.draw_handler_remove(self._handle_3d, 'WINDOW')
            except:
                pass;
            ui_props = bpy.context.scene.blenderkitUI
    
            ui_props.dragging = False
            ui_props.tooltip = ''
            ui_props.active_index = -3
            ui_props.draw_drag_image = False
            ui_props.draw_snapped_bounds = False
            ui_props.has_hit = False
            ui_props.assetbar_on = False
    
        def modal(self, context, event):
            # This is for case of closing the area or changing type:
            ui_props = context.scene.blenderkitUI
            user_preferences = bpy.context.preferences.addons['blenderkit'].preferences
    
            areas = []
    
    
    
            #timers testing - seems timers might be causing crashes. testing it this way now.
            if not user_preferences.use_timers:
                    search.timer_update()
                    download.timer_update()
                    tasks_queue.queue_worker()
                    bg_blender.bg_update()
    
    
    Vilem Duha's avatar
    Vilem Duha committed
            if bpy.context.scene != self.scene:
                self.exit_modal()
                return {'CANCELLED'}
    
    
            for w in context.window_manager.windows:
                areas.extend(w.screen.areas)
    
            if self.area not in areas or self.area.type != 'VIEW_3D' or self.has_quad_views != (
                    len(self.area.spaces[0].region_quadviews) > 0):
                # print('search areas')   bpy.context.area.spaces[0].region_quadviews
    
    Vilem Duha's avatar
    Vilem Duha committed
                # stopping here model by now - because of:
                #   switching layouts or maximizing area now fails to assign new area throwing the bug
                #   internal error: modal gizmo-map handler has invalid area
                self.exit_modal()
                return {'CANCELLED'}
    
                newarea = None
                for a in context.window.screen.areas:
                    if a.type == 'VIEW_3D':
                        self.area = a
                        for r in a.regions:
                            if r.type == 'WINDOW':
                                self.region = r
                        newarea = a
                        break;
                        # context.area = a
    
                # we check again and quit if things weren't fixed this way.
                if newarea == None:
                    self.exit_modal()
                    return {'CANCELLED'}
    
            update_ui_size(self.area, self.region)
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    
            # this was here to check if sculpt stroke is running, but obviously that didn't help,
            #  since the RELEASE event is cought by operator and thus there is no way to detect a stroke has ended...
            if bpy.context.mode in ('SCULPT', 'PAINT_TEXTURE'):
                if event.type == 'MOUSEMOVE':  # ASSUME THAT SCULPT OPERATOR ACTUALLY STEALS THESE EVENTS,
                    # SO WHEN THERE ARE SOME WE CAN APPEND BRUSH...
                    bpy.context.window_manager['appendable'] = True
                if event.type == 'LEFTMOUSE':
                    if event.value == 'PRESS':
                        bpy.context.window_manager['appendable'] = False
    
            self.area.tag_redraw()
            s = context.scene
    
            if ui_props.turn_off:
                ui_props.turn_off = False
                self.exit_modal()
                ui_props.draw_tooltip = False
                return {'CANCELLED'}
    
    
            if context.region != self.region:
                # print(time.time(), 'pass through because of region')
                # print(context.region.type, self.region.type)
                return {'PASS_THROUGH'}
    
    
    Vilem Duha's avatar
    Vilem Duha committed
            if ui_props.down_up == 'UPLOAD':
    
                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
                if (
                        event.type == 'LEFTMOUSE' or event.type == 'RIGHTMOUSE') and event.value == 'RELEASE' or event.type == 'ENTER' or ui_props.tooltip == '':
                    ao = bpy.context.active_object
                    if ui_props.asset_type == 'MODEL' and ao != None \
                            or ui_props.asset_type == 'MATERIAL' and ao != None and ao.active_material != None \
    
                            or ui_props.asset_type == 'BRUSH' and utils.get_active_brush() is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                        export_data, upload_data, eval_path_computing, eval_path_state, eval_path, props = upload.get_upload_data(
                            self,
                            context,
                            ui_props.asset_type)
                        ui_props.tooltip = search.generate_tooltip(upload_data)
    
                return {'PASS_THROUGH'}
    
            # TODO add one more condition here to take less performance.
            r = self.region
            s = bpy.context.scene
            sr = s.get('search results')
    
            search_results_orig = s.get('search results orig')
    
    Vilem Duha's avatar
    Vilem Duha committed
            # If there aren't any results, we need no interaction(yet)
            if sr is None:
                return {'PASS_THROUGH'}
            if len(sr) - ui_props.scrolloffset < (ui_props.wcount * ui_props.hcount) + 10:
                self.search_more()
            if event.type == 'WHEELUPMOUSE' or event.type == 'WHEELDOWNMOUSE' or event.type == 'TRACKPADPAN':
                # scrolling
                mx = event.mouse_region_x
                my = event.mouse_region_y
    
                if ui_props.dragging and not mouse_in_asset_bar(mx, my):  # and my < r.height - ui_props.bar_height \
                    # and mx > 0 and mx < r.width and my > 0:
                    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.
                    ui_props.has_hit, ui_props.snapped_location, ui_props.snapped_normal, ui_props.snapped_rotation, face_index, object, matrix = mouse_raycast(
                        context, mx, my)
    
                    # MODELS can be dragged on scene floor
                    if not ui_props.has_hit and ui_props.asset_type == 'MODEL':
                        ui_props.has_hit, ui_props.snapped_location, ui_props.snapped_normal, ui_props.snapped_rotation, face_index, object, matrix = floor_raycast(
                            context,
                            mx, my)
    
                    return {'RUNNING_MODAL'}
    
                if not mouse_in_asset_bar(mx, my):
                    return {'PASS_THROUGH'}
    
                # note - TRACKPADPAN is unsupported in blender by now.
                # if event.type == 'TRACKPADPAN' :
                #     print(dir(event))
                #     print(event.value, event.oskey, event.)
    
                if (event.type == 'WHEELDOWNMOUSE') and len(sr) - ui_props.scrolloffset > (
                        ui_props.wcount * ui_props.hcount):
                    if ui_props.hcount > 1:
                        ui_props.scrolloffset += ui_props.wcount
                    else:
                        ui_props.scrolloffset += 1
                    if len(sr) - ui_props.scrolloffset < (ui_props.wcount * ui_props.hcount):
                        ui_props.scrolloffset = len(sr) - (ui_props.wcount * ui_props.hcount)
    
    Vilem Duha's avatar
    Vilem Duha committed
    
                if event.type == 'WHEELUPMOUSE' and ui_props.scrolloffset > 0:
    
                    if ui_props.hcount > 1:
                        ui_props.scrolloffset -= ui_props.wcount
                    else:
                        ui_props.scrolloffset -= 1
                    if ui_props.scrolloffset < 0:
                        ui_props.scrolloffset = 0
    
    
    Vilem Duha's avatar
    Vilem Duha committed
                return {'RUNNING_MODAL'}
            if event.type == 'MOUSEMOVE':  # Apply
    
                r = self.region
                mx = event.mouse_region_x
                my = event.mouse_region_y
    
                ui_props.mouse_x = mx
                ui_props.mouse_y = my
    
                if ui_props.dragging_rating or ui_props.rating_menu_on:
                    res = interact_rating(r, mx, my, event)
                    if res == True:
                        return {'RUNNING_MODAL'}
    
                if ui_props.drag_init:
                    ui_props.drag_length += 1
                    if ui_props.drag_length > 0:
                        ui_props.dragging = True
                        ui_props.drag_init = False
    
    
                if not (ui_props.dragging and mouse_in_region(r, mx, my)) and not mouse_in_asset_bar(mx, my):  #
    
                    ui_props.dragging = False
                    ui_props.has_hit = False
    
    Vilem Duha's avatar
    Vilem Duha committed
                    ui_props.active_index = -3
    
                    ui_props.draw_drag_image = False
                    ui_props.draw_snapped_bounds = False
    
    Vilem Duha's avatar
    Vilem Duha committed
                    ui_props.draw_tooltip = False
                    bpy.context.window.cursor_set("DEFAULT")
                    return {'PASS_THROUGH'}
    
                sr = bpy.context.scene['search results']
    
                if not ui_props.dragging:
                    bpy.context.window.cursor_set("DEFAULT")
    
                    if sr != None and ui_props.wcount * ui_props.hcount > len(sr) and ui_props.scrolloffset > 0:
                        ui_props.scrolloffset = 0
    
                    asset_search_index = get_asset_under_mouse(mx, my)
                    ui_props.active_index = asset_search_index
                    if asset_search_index > -1:
    
                        asset_data = sr[asset_search_index]
                        ui_props.draw_tooltip = True
    
                        ui_props.tooltip = asset_data['tooltip']
    
                        # bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu')
    
    Vilém Duha's avatar
    Vilém Duha committed
    
    
    Vilem Duha's avatar
    Vilem Duha committed
                    else:
                        ui_props.draw_tooltip = False
    
    
                    if mx > ui_props.bar_x + ui_props.bar_width - 50 and search_results_orig[
                        'count'] - ui_props.scrolloffset > (
                            ui_props.wcount * ui_props.hcount) + 1:
    
    Vilem Duha's avatar
    Vilem Duha committed
                        ui_props.active_index = -1
                        return {'RUNNING_MODAL'}
                    if mx < ui_props.bar_x + 50 and ui_props.scrolloffset > 0:
                        ui_props.active_index = -2
                        return {'RUNNING_MODAL'}
    
                else:
                    result = False
    
                    if ui_props.dragging and mouse_in_region(r, mx, my):
    
    Vilem Duha's avatar
    Vilem Duha committed
                        ui_props.has_hit, ui_props.snapped_location, ui_props.snapped_normal, ui_props.snapped_rotation, face_index, object, matrix = mouse_raycast(
                            context, mx, my)
                        # MODELS can be dragged on scene floor
                        if not ui_props.has_hit and ui_props.asset_type == 'MODEL':
                            ui_props.has_hit, ui_props.snapped_location, ui_props.snapped_normal, ui_props.snapped_rotation, face_index, object, matrix = floor_raycast(
                                context,
                                mx, my)
                    if ui_props.has_hit and ui_props.asset_type == 'MODEL':
                        # this condition is here to fix a bug for a scene submitted by a user, so this situation shouldn't
                        # happen anymore, but there might exists scenes which have this problem for some reason.
    
                        if ui_props.active_index < len(sr) and ui_props.active_index > -1:
    
    Vilem Duha's avatar
    Vilem Duha committed
                            ui_props.draw_snapped_bounds = True
                            active_mod = sr[ui_props.active_index]
                            ui_props.snapped_bbox_min = Vector(active_mod['bbox_min'])
                            ui_props.snapped_bbox_max = Vector(active_mod['bbox_max'])
    
                    else:
                        ui_props.draw_snapped_bounds = False
                        ui_props.draw_drag_image = True
                return {'RUNNING_MODAL'}
    
    
            if event.type == 'RIGHTMOUSE':
                mx = event.mouse_x - r.x
                my = event.mouse_y - r.y
    
                if event.value == 'PRESS' and mouse_in_asset_bar(mx, my):
                    bpy.ops.wm.call_menu(name='OBJECT_MT_blenderkit_asset_menu')
                    return {'RUNNING_MODAL'}
    
    
    Vilem Duha's avatar
    Vilem Duha committed
            if event.type == 'LEFTMOUSE':
    
                r = self.region
                mx = event.mouse_x - r.x
                my = event.mouse_y - r.y
    
                ui_props = context.scene.blenderkitUI
                if event.value == 'PRESS' and ui_props.active_index > -1:
                    if ui_props.asset_type == 'MODEL' or ui_props.asset_type == 'MATERIAL':
    
                        # check if asset is locked and let the user know in that case
                        asset_search_index = ui_props.active_index
                        asset_data = sr[asset_search_index]
    
                        if not asset_data.get('canDownload'):
    
                            message = "Let's support asset creators and Blender development."
                            link_text = 'Unlock the asset.'
    
    Vilém Duha's avatar
    Vilém Duha committed
                            url = paths.get_bkit_url() + '/get-blenderkit/' + asset_data['id'] + '/?from_addon'
    
                            bpy.ops.wm.blenderkit_url_dialog('INVOKE_REGION_WIN', url=url, message=message,
                                                             link_text=link_text)
                            return {'RUNNING_MODAL'}
                        # go on with drag init
    
    Vilem Duha's avatar
    Vilem Duha committed
                        ui_props.drag_init = True
                        bpy.context.window.cursor_set("NONE")
                        ui_props.draw_tooltip = False
                        ui_props.drag_length = 0
    
                if ui_props.rating_on:
                    res = interact_rating(r, mx, my, event)
                    if res:
                        return {'RUNNING_MODAL'}
    
                if not ui_props.dragging and not mouse_in_asset_bar(mx, my):
                    return {'PASS_THROUGH'}
    
                # this can happen by switching result asset types - length of search result changes
                if ui_props.scrolloffset > 0 and (ui_props.wcount * ui_props.hcount) > len(sr) - ui_props.scrolloffset:
                    ui_props.scrolloffset = len(sr) - (ui_props.wcount * ui_props.hcount)
    
                if event.value == 'RELEASE':  # Confirm
                    ui_props.drag_init = False
    
                    # scroll by a whole page
                    if mx > ui_props.bar_x + ui_props.bar_width - 50 and len(
                            sr) - ui_props.scrolloffset > ui_props.wcount * ui_props.hcount:
                        ui_props.scrolloffset = min(
                            ui_props.scrolloffset + (ui_props.wcount * ui_props.hcount),
                            len(sr) - ui_props.wcount * ui_props.hcount)
                        return {'RUNNING_MODAL'}
                    if mx < ui_props.bar_x + 50 and ui_props.scrolloffset > 0:
                        ui_props.scrolloffset = max(0, ui_props.scrolloffset - ui_props.wcount * ui_props.hcount)
                        return {'RUNNING_MODAL'}
    
                    # Drag-drop interaction
                    if ui_props.dragging and mouse_in_region(r, mx, my):
                        asset_search_index = ui_props.active_index
                        # raycast here
                        ui_props.active_index = -3
    
                        if ui_props.asset_type == 'MODEL':
    
                            ui_props.has_hit, ui_props.snapped_location, ui_props.snapped_normal, ui_props.snapped_rotation, face_index, object, matrix = mouse_raycast(
                                context, mx, my)
    
                            # MODELS can be dragged on scene floor
                            if not ui_props.has_hit and ui_props.asset_type == 'MODEL':
                                ui_props.has_hit, ui_props.snapped_location, ui_props.snapped_normal, ui_props.snapped_rotation, face_index, object, matrix = floor_raycast(
                                    context,
                                    mx, my)
    
                            if not ui_props.has_hit:
                                return {'RUNNING_MODAL'}
    
                            target_object = ''
                            if object is not None:
                                target_object = object.name
                            target_slot = ''
    
                        if ui_props.asset_type == 'MATERIAL':
                            ui_props.has_hit, ui_props.snapped_location, ui_props.snapped_normal, ui_props.snapped_rotation, face_index, object, matrix = mouse_raycast(
                                context, mx, my)
    
                            if not ui_props.has_hit:
                                # this is last attempt to get object under mouse - for curves and other objects than mesh.
                                ui_props.dragging = False
                                sel = utils.selection_get()
                                bpy.ops.view3d.select(location=(event.mouse_region_x, event.mouse_region_y))
                                sel1 = utils.selection_get()
                                if sel[0] != sel1[0] and sel1[0].type != 'MESH':
                                    object = sel1[0]
                                    target_slot = sel1[0].active_material_index
                                    ui_props.has_hit = True
                                utils.selection_set(sel)
    
                            if not ui_props.has_hit:
                                return {'RUNNING_MODAL'}
    
                            else:
                                # first, test if object can have material applied.
    
                                # TODO add other types here if droppable.
    
                                if object is not None and not object.is_library_indirect and object.type == 'MESH':
    
    Vilem Duha's avatar
    Vilem Duha committed
                                    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()
    
    Vilem Duha's avatar
    Vilem Duha committed
                                    target_slot = temp_mesh.polygons[face_index].material_index
    
                                    object_eval.to_mesh_clear()
    
    Vilem Duha's avatar
    Vilem Duha committed
                                else:
                                    self.report({'WARNING'}, "Invalid or library object as input:")
                                    target_object = ''
                                    target_slot = ''
    
                    # Click interaction
                    else:
                        asset_search_index = get_asset_under_mouse(mx, my)
    
                        if ui_props.asset_type in ('MATERIAL',
                                                   'MODEL'):  # this was meant for particles, commenting for now or ui_props.asset_type == 'MODEL':
                            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
                            else:
                                target_object = ''
                                target_slot = ''
                    # FIRST START SEARCH
    
                    if asset_search_index == -3:
                        return {'RUNNING_MODAL'}
                    if asset_search_index > -3:
                        if ui_props.asset_type == 'MATERIAL':
                            if target_object != '':
                                # position is for downloader:
                                loc = ui_props.snapped_location
                                rotation = (0, 0, 0)
    
                                asset_data = sr[asset_search_index]
                                utils.automap(target_object, target_slot=target_slot,
                                              tex_size=asset_data.get('texture_size_meters', 1.0))
                                bpy.ops.scene.blenderkit_download(True,
                                                                  asset_type=ui_props.asset_type,
                                                                  asset_index=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 ui_props.has_hit and ui_props.dragging:
                                loc = ui_props.snapped_location
                                rotation = ui_props.snapped_rotation
                            else:
                                loc = s.cursor.location
                                rotation = s.cursor.rotation_euler
    
                            bpy.ops.scene.blenderkit_download(True,
                                                              asset_type=ui_props.asset_type,
                                                              asset_index=asset_search_index,
                                                              model_location=loc,
                                                              model_rotation=rotation,
                                                              target_object=target_object)
    
                        else:
                            bpy.ops.scene.blenderkit_download(asset_type=ui_props.asset_type,
                                                              asset_index=asset_search_index)
    
                        ui_props.dragging = False
                        return {'RUNNING_MODAL'}
                else:
                    return {'RUNNING_MODAL'}
    
    
    Vilém Duha's avatar
    Vilém Duha committed
            if event.type == 'W' and ui_props.active_index > -1:
    
    Vilem Duha's avatar
    Vilem Duha committed
                sr = bpy.context.scene['search results']
                asset_data = sr[ui_props.active_index]
    
                a = bpy.context.window_manager['bkit authors'].get(asset_data['author']['id'])
    
    Vilem Duha's avatar
    Vilem Duha committed
                if a is not None:
    
    Vilem Duha's avatar
    Vilem Duha committed
                    if a.get('aboutMeUrl') is not None:
                        bpy.ops.wm.url_open(url=a['aboutMeUrl'])
    
    Vilém Duha's avatar
    Vilém Duha committed
            if event.type == 'A' and ui_props.active_index > -1:
    
                sr = bpy.context.scene['search results']
                asset_data = sr[ui_props.active_index]
    
                    sprops = utils.get_search_props()
                    sprops.search_keywords = ''
    
                    sprops.search_verification_status = 'ALL'
    
                    utils.p('author:', a)
    
                    search.search(author_id=a)
    
                return {'RUNNING_MODAL'}
    
    Vilém Duha's avatar
    Vilém Duha committed
            if event.type == 'X' and ui_props.active_index > -1:
    
                # delete downloaded files for this asset
    
    Vilem Duha's avatar
    Vilem Duha committed
                sr = bpy.context.scene['search results']
                asset_data = sr[ui_props.active_index]
                print(asset_data['name'])
                print('delete')
                paths.delete_asset_debug(asset_data)
                asset_data['downloaded'] = 0
                return {'RUNNING_MODAL'}
            return {'PASS_THROUGH'}
    
        def invoke(self, context, event):
            # FIRST START SEARCH
            ui_props = context.scene.blenderkitUI
    
            if self.do_search:
                # 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)
    
    Vilem Duha's avatar
    Vilem Duha committed
    
            if ui_props.assetbar_on:
                # 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.
    
    Vilem Duha's avatar
    Vilem Duha committed
                    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 {'FINISHED'}
    
    
            ui_props.dragging = False  # only for cases where assetbar ended with an error.
    
    Vilem Duha's avatar
    Vilem Duha committed
            ui_props.assetbar_on = True
            ui_props.turn_off = False
    
            sr = bpy.context.scene.get('search results')
            if sr is None:
                bpy.context.scene['search results'] = []
    
    
            if context.area.type != 'VIEW_3D':
                self.report({'WARNING'}, "View3D not found, cannot run operator")
                return {'CANCELLED'}
    
            # the arguments we pass the the callback
            args = (self, context)
    
            self.window = context.window
            self.area = context.area
            self.scene = bpy.context.scene
    
            self.has_quad_views = len(bpy.context.area.spaces[0].region_quadviews) > 0
    
            for r in self.area.regions:
                if r.type == 'WINDOW':
                    self.region = r
    
            global active_window, active_area, active_region
            active_window = self.window
            active_area = self.area
            active_region = self.region
    
            update_ui_size(self.area, self.region)
    
            self._handle_2d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_2d, args, 'WINDOW', 'POST_PIXEL')
            self._handle_3d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d, args, 'WINDOW', 'POST_VIEW')
    
            context.window_manager.modal_handler_add(self)
            ui_props.assetbar_on = True
            return {'RUNNING_MODAL'}
    
    Vilem Duha's avatar
    Vilem Duha committed
    
        def execute(self, context):
            return {'RUNNING_MODAL'}
    
    
    
    Vilém Duha's avatar
    Vilém Duha committed
    class TransferBlenderkitData(bpy.types.Operator):
        """Regenerate cobweb"""
        bl_idname = "object.blenderkit_data_trasnfer"
        bl_label = "Transfer BlenderKit data"
        bl_description = "Transfer blenderKit metadata from one object to another when fixing uploads with wrong parenting."
        bl_options = {'REGISTER', 'UNDO'}
    
        def execute(self, context):
            source_ob = bpy.context.active_object
            for target_ob in bpy.context.selected_objects:
                if target_ob != source_ob:
                    target_ob.property_unset('blenderkit')
                    for k in source_ob.keys():
                        target_ob[k] = source_ob[k]
            source_ob.property_unset('blenderkit')
            return {'FINISHED'}
    
    
    class UndoWithContext(bpy.types.Operator):
        """Regenerate cobweb"""
        bl_idname = "wm.undo_push_context"
        bl_label = "BlnenderKit undo push"
        bl_description = "BlenderKit undo push with fixed context"
        bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
    
        # def modal(self, context, event):
        #     return {'RUNNING_MODAL'}
    
    
        message: StringProperty('Undo Message', default='BlenderKit operation')
    
        def execute(self, context):
    
            C_dict = utils.get_fake_context(context)
    
    Vilém Duha's avatar
    Vilém Duha committed
            #w, a, r = get_largest_area(area_type=area_type)
            # wm = bpy.context.window_manager#bpy.data.window_managers[0]
            # w = wm.windows[0]
            #
            # C_dict = {'window': w, 'screen': w.screen}
    
            # bpy.ops.ed.undo_push(C_dict, 'INVOKE_REGION_WIN', message=self.message)
            bpy.ops.ed.undo_push( 'INVOKE_REGION_WIN', message=self.message)
    
    class RunAssetBarWithContext(bpy.types.Operator):
        """Regenerate cobweb"""
        bl_idname = "object.run_assetbar_fix_context"
        bl_label = "BlnenderKit assetbar with fixed context"
        bl_description = "Run assetbar with fixed context"
    
        bl_options = {'REGISTER', 'UNDO', 'INTERNAL'}
    
    
        # def modal(self, context, event):
        #     return {'RUNNING_MODAL'}
    
    
            C_dict = utils.get_fake_context(context)
    
            bpy.ops.view3d.blenderkit_asset_bar(C_dict, 'INVOKE_REGION_WIN', keep_running=True, do_search=False)
    
            return {'FINISHED'}
    
    Vilém Duha's avatar
    Vilém Duha committed
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    classess = (
        AssetBarOperator,
    
        TransferBlenderkitData,
        UndoWithContext
    
    # store keymap items here to access after registration
    addon_keymapitems = []
    
    def pre_load(context):
        ui_props = bpy.context.scene.blenderkitUI
        ui_props.assetbar_on = False
        ui_props.turn_off = True
        preferences = bpy.context.preferences.addons['blenderkit'].preferences
        preferences.login_attempt = False
    
    
    
    Vilem Duha's avatar
    Vilem Duha committed
    def register_ui():
    
    Vilem Duha's avatar
    Vilem Duha committed
        for c in classess:
            bpy.utils.register_class(c)
    
        args = (None, bpy.context)
    
    
        handler_2d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_2d_progress, args, 'WINDOW', 'POST_PIXEL')
        handler_3d = bpy.types.SpaceView3D.draw_handler_add(draw_callback_3d_progress, args, 'WINDOW', 'POST_VIEW')
    
    Vilem Duha's avatar
    Vilem Duha committed
    
        wm = bpy.context.window_manager
    
        # spaces solved by registering shortcut to Window. Couldn't register object mode before somehow.
        if not wm.keyconfigs.addon:
            return
        km = wm.keyconfigs.addon.keymaps.new(name="Window", space_type='EMPTY')
    
        #asset bar shortcut
    
    Vilem Duha's avatar
    Vilem Duha committed
        kmi = km.keymap_items.new(AssetBarOperator.bl_idname, 'SEMI_COLON', 'PRESS', ctrl=False, shift=False)
        kmi.properties.keep_running = False
        kmi.properties.do_search = False
    
        #fast rating shortcut
        wm = bpy.context.window_manager
        km = wm.keyconfigs.addon.keymaps['Window']
        kmi = km.keymap_items.new(ratings.FastRateMenu.bl_idname, 'F', 'PRESS', ctrl=False, shift=False)
    
    Vilem Duha's avatar
    Vilem Duha committed
    
    
    def unregister_ui():
    
        global handler_2d, handler_3d
    
        pre_load(bpy.context)
    
        bpy.types.SpaceView3D.draw_handler_remove(handler_2d, 'WINDOW')
        bpy.types.SpaceView3D.draw_handler_remove(handler_3d, 'WINDOW')
    
    
    Vilem Duha's avatar
    Vilem Duha committed
        for c in classess:
            bpy.utils.unregister_class(c)
    
        wm = bpy.context.window_manager
        if not wm.keyconfigs.addon:
            return
    
    
        km = wm.keyconfigs.addon.keymaps['Window']
        for kmi in addon_keymapitems:
            km.keymap_items.remove(kmi)
        del addon_keymapitems[:]