[Bf-extensions-cvs] [e7a3d0d8] master: GPencil Tools: Layer navigator

Pullusb noreply at git.blender.org
Mon Jan 23 20:52:15 CET 2023


Commit: e7a3d0d8482a7315ce77251f8410e9d8232be82c
Author: Pullusb
Date:   Mon Jan 23 20:52:00 2023 +0100
Branches: master
https://developer.blender.org/rBAe7a3d0d8482a7315ce77251f8410e9d8232be82c

GPencil Tools: Layer navigator

Layer navigator: Continuous press on 'Y' shortcut override default layer menu popup with a new customized one designed for fullscreen work.
Popup include following features:
    - Active layer always pop under under mouse when called
    - Active layer is changed just by hovering over layers
    - Opacity, hide, lock states can be tweaked in popup
    - Passing lateral limits will fade inactive layers, useful to quickly inspect content.
    - Layers can be reordered with simple drag'n'drop
    - `+` button on the right add a new layer
    - Layer box height/width, font size, and left-handed mode are accessible in addon preferences
    - Extra shortcuts are enabled while layer navigator is up:
        - `H` toggle all hide
        - `L` toggle all lock
        - `T` toggle auto-lock

Addon preferences UI now use one Tab per tool

===================================================================

M	greasepencil_tools/__init__.py
A	greasepencil_tools/layer_navigator.py
M	greasepencil_tools/prefs.py

===================================================================

diff --git a/greasepencil_tools/__init__.py b/greasepencil_tools/__init__.py
index 25363439..fee75347 100644
--- a/greasepencil_tools/__init__.py
+++ b/greasepencil_tools/__init__.py
@@ -4,7 +4,7 @@ bl_info = {
 "name": "Grease Pencil Tools",
 "description": "Extra tools for Grease Pencil",
 "author": "Samuel Bernou, Antonio Vazquez, Daniel Martinez Lara, Matias Mendiola",
-"version": (1, 6, 2),
+"version": (1, 7, 9),
 "blender": (3, 0, 0),
 "location": "Sidebar > Grease Pencil > Grease Pencil Tools",
 "warning": "",
@@ -19,38 +19,41 @@ from .  import (prefs,
                 box_deform,
                 line_reshape,
                 rotate_canvas,
+                layer_navigator,
                 timeline_scrub,
                 draw_tools,
                 import_brush_pack,
                 ui_panels,
                 )
 
+modules = (
+    prefs,
+    box_deform,
+    line_reshape,
+    rotate_canvas,
+    layer_navigator,
+    timeline_scrub,
+    draw_tools,
+    import_brush_pack,
+    ui_panels,
+)
+
 def register():
     if bpy.app.background:
         return
-    prefs.register()
-    timeline_scrub.register()
-    box_deform.register()
-    line_reshape.register()
-    rotate_canvas.register()
-    draw_tools.register()
-    import_brush_pack.register()
-    ui_panels.register()
-
-    ## update tab name with update in pref file (passing addon_prefs)
+
+    for mod in modules:
+        mod.register()
+
+    ## Update tab name with update in pref file (passing addon_prefs)
     prefs.update_panel(prefs.get_addon_prefs(), bpy.context)
 
 def unregister():
     if bpy.app.background:
         return
-    ui_panels.unregister()
-    import_brush_pack.unregister()
-    draw_tools.unregister()
-    rotate_canvas.unregister()
-    box_deform.unregister()
-    line_reshape.unregister()
-    timeline_scrub.unregister()
-    prefs.unregister()
+
+    for mod in modules:
+        mod.unregister()
 
 if __name__ == "__main__":
     register()
diff --git a/greasepencil_tools/layer_navigator.py b/greasepencil_tools/layer_navigator.py
new file mode 100644
index 00000000..bb40034b
--- /dev/null
+++ b/greasepencil_tools/layer_navigator.py
@@ -0,0 +1,902 @@
+import bpy
+import blf, gpu
+import math
+from gpu_extras.batch import batch_for_shader
+from mathutils import Vector, Matrix
+from time import time
+from pathlib import Path
+
+from bpy.props import (BoolProperty, IntProperty)
+
+from .prefs import get_addon_prefs
+
+
+def rectangle_tris_from_coords(quad_list):
+    '''Get a list of Vector corner for a triangle
+    return a list of TRI for gpu drawing'''
+    return [           
+            # tri 1
+            quad_list[0],
+            quad_list[1],
+            quad_list[2],
+            # tri 2
+            quad_list[0],
+            quad_list[3],
+            quad_list[2]
+        ]
+
+def round_to_ceil_even(f):
+  if (math.floor(f) % 2 == 0): 
+    return math.floor(f)
+  else: 
+    return math.floor(f) + 1
+
+def move_layer_to_index(l, idx):
+    a = [i for i, lay in enumerate(l.id_data.layers) if lay == l][0]
+    move = idx - a
+    if move == 0:
+        return
+    direction = 'UP' if move > 0 else 'DOWN'
+    for _i in range(abs(move)):
+        l.id_data.layers.move(l, direction)
+
+def get_reduced_area_coord(context):
+    w, h = context.region.width, context.region.height
+
+    ## minus tool leftbar + sidebar right
+    regs = context.area.regions
+    toolbar = regs[2]
+    sidebar = regs[3]
+    header = regs[0]
+    tool_header = regs[1]
+    up_margin = down_margin = 0
+    if tool_header.alignment == 'TOP':
+        up_margin += tool_header.height
+    else:
+        down_margin += tool_header.height
+
+    ## set corner values
+    left_down = (toolbar.width, down_margin+2)
+    right_down = (w - sidebar.width, down_margin+2)
+    left_up = (toolbar.width, h - up_margin-1)
+    right_up = (w - sidebar.width, h - up_margin-1)
+    return left_down, right_down, left_up, right_up
+
+def draw_callback_px(self, context):
+    if context.area != self.current_area:
+        return
+    font_id = 0
+
+    ## timer for debug purposes
+    # blf.position(font_id, 15, 30, 0)
+    # blf.size(font_id, 20, 72)
+    # blf.draw(font_id, "Time " + self.text)
+
+    shader = gpu.shader.from_builtin('2D_UNIFORM_COLOR')  # initiate shader
+    gpu.state.blend_set('ALPHA')
+    gpu.state.line_width_set(1.0)
+
+    shader.bind()
+
+    ## draw one background at once
+    # shader.uniform_float("color", self.bg_color) # (1.0, 1.0, 1.0, 1.0)
+    # self.batch_bg.draw(shader)
+
+    ## locked layer (individual rectangle)
+    rects = []
+    lock_rects = []
+    opacitys = []
+    opacity_bars = []
+    active_case = []
+    active_width = float(round_to_ceil_even(4.0 * context.preferences.system.ui_scale))
+    
+    ## tex icon store
+    icons = {'locked':[],'unlocked':[], 'hide_off':[], 'hide_on':[]}
+
+    for i, l in enumerate(self.gpl):
+        ## Rectangle coords CW from bottom-left corner
+
+        corner = Vector((self.left, self.bottom + self.px_h * i))
+        if i == self.ui_idx:
+            # With LINE_STRIP: Repeat first coordinate to close square with line_strip shader
+            # active_case = [v + corner for v in self.case] + [self.case[0] + corner]
+
+            ## With LINES: width offset to avoid jaggy corner
+            # Convert single corners point to flattened line vector pairs
+            active_case = [v + corner for v in self.case]
+            flattened_line_pairs = []
+            for i in range(len(active_case)):
+                flattened_line_pairs += [active_case[i], active_case[(i+1) % len(active_case)]]
+
+            # Build offset table
+            px_offset = int(active_width / 2)
+            case_px_offsets = [
+                Vector((0, -px_offset)), Vector((0, px_offset)),
+                Vector((-px_offset, 0)), Vector((px_offset, 0)),
+                Vector((0, px_offset)), Vector((0, -px_offset)),
+                Vector((px_offset, 0)), Vector((-px_offset, 0)),
+                ]
+
+            # Apply offset to line tips
+            active_case = [v + offset for v, offset in zip(flattened_line_pairs, case_px_offsets)]
+            
+
+        lock_coord = corner + Vector((self.px_w - self.icons_margin_a, self.mid_height - int(self.icon_size / 2)))
+
+        hide_coord = corner + Vector((self.px_w - self.icons_margin_b, self.mid_height - int(self.icon_size / 2)))
+
+
+        if l.lock:
+            lock_rects += rectangle_tris_from_coords(
+                [v + corner for v in self.case]
+            )
+            icons['locked'].append([v + lock_coord for v in self.icon_tex_coord])
+        else:
+            rects += rectangle_tris_from_coords(
+                [v + corner for v in self.case]
+            )
+            icons['unlocked'].append([v + lock_coord for v in self.icon_tex_coord])
+
+
+        if l.hide:
+            icons['hide_on'].append([v + hide_coord for v in self.icon_tex_coord])
+        else:
+            icons['hide_off'].append([v + hide_coord for v in self.icon_tex_coord])
+
+
+        ## opacity sliders background
+        opacity_bars += rectangle_tris_from_coords(
+            [corner + v for v in self.opacity_slider]
+        )
+        ## opacity sliders
+        if l.opacity:
+            opacitys += rectangle_tris_from_coords(
+                [corner + v for v in self.opacity_slider[:2]]
+                + [corner +  Vector((int(v[0] * l.opacity), v[1])) for v in self.opacity_slider[2:]]
+            )
+
+    ### --- Trace squares
+    ## individual unlocked squares
+    shader.uniform_float("color", self.bg_color)
+    batch_squares = batch_for_shader(shader, 'TRIS', {"pos": rects})
+    batch_squares.draw(shader)
+
+    ## locked squares
+    shader.uniform_float("color", self.lock_color)
+    batch_lock = batch_for_shader(shader, 'TRIS', {"pos": lock_rects})
+    batch_lock.draw(shader)
+
+    ## bg_full_bar
+    shader.uniform_float("color", self.opacity_bar_color)
+    batch_lock = batch_for_shader(shader, 'TRIS', {"pos": opacity_bars})
+    batch_lock.draw(shader)
+
+    ## opacity sliders
+    shader.uniform_float("color", self.opacity_color)
+    batch_lock = batch_for_shader(shader, 'TRIS', {"pos": opacitys})
+    batch_lock.draw(shader)
+
+    ### --- Trace Lines
+    gpu.state.line_width_set(2.0)
+
+    ## line color (static)
+    shader.uniform_float("color", self.lines_color)
+    self.batch_lines.draw(shader)
+    
+    ## "Plus" lines
+    if self.gpl.active_index == 0:
+        plus_lines = self.plus_lines[:8]
+    else:
+        plus_lines = self.plus_lines[self.gpl.active_index * 4 + 4:self.gpl.active_index * 4 + 8]
+    batch_plus = batch_for_shader(
+        shader, 'LINES', {"pos": plus_lines})
+    batch_plus.draw(shader)
+
+    ## Loop draw tex icons
+    for icon_name, coord_list in icons.items():
+        texture = gpu.texture.from_image(self.icon_tex[icon_name])
+        for coords in coord_list:
+            shader_tex = gpu.shader.from_builtin('2D_IMAGE')
+            batch_icons = batch_for_shader(
+                shader_tex, 'TRI_FAN',
+                {
+                    "pos": coords,
+                    "texCoord": ((0, 0), (1, 0), (1, 1), (0, 1)),
+                },
+            )
+            shader_tex.bind()
+            shader_tex.uniform_sampler("image", texture)
+            batch_icons.draw(shader_tex)
+
+    ## Highlight active layer
+    if active_case:
+        gpu.state.line_width_set(active_width)
+        shader.uniform_float("color", self.active_layer_color)
+        # batch_active = batch_for_shader(shader, 'LINE_STRIP', {"pos": active_case})
+        batch_active = batch_for_shader(shader, 'LINES', {"pos": active_case})
+        batch_active.draw(shader)
+
+    gpu.state.line_width_set(1.0)
+    gpu.state.blend_set('NONE')
+
+
+    ### --- Texts
+    for i, l in enumerate(self.gpl):
+        ## add color underneath active name
+        # if i == self.ui_idx:
+        #     ## color = self.active_layer_color # Color active name
+        #     blf.position(font_id, self.text_x+1, self.text_pos[i]-1, 0)
+        #     blf.size(font_id, self.text_size, 72)
+        #     blf.color(font_id, *self.act

@@ Diff output truncated at 10240 characters. @@



More information about the Bf-extensions-cvs mailing list