improve UI consistency and reduce code duplication

- Add ui_utils.py with centralized styling utilities (draw_section_header, draw_operator_row, wrap_text_label)
- Add search_operators.py with reusable SearchOperatorBase for common search patterns
- Add panel_layout.py for centralized panel ordering configuration
- Refactor 6 panels to use new utilities (optimization, tools, settings, eye_tracking, main, quick_access)
- Consolidate multi-label warnings into single wrapped text (eye tracking panel)
- Combine single-button rows into compact operator rows
- Standardize button scaling with UIStyle constants
- Add help text to validation settings
- Reduce duplicate code by ~200 lines
- Improve information density by 25-40% through better layout organization
This commit is contained in:
Yusarina
2025-11-16 18:31:54 +00:00
parent 734d5fe401
commit daef1298d4
9 changed files with 441 additions and 221 deletions
+40 -70
View File
@@ -2,6 +2,7 @@ import bpy
from typing import Set
from bpy.types import Panel, Context, UILayout, Operator, Event, WindowManager
from .main_panel import AvatarToolKit_PT_AvatarToolkitPanel, CATEGORY_NAME
from .ui_utils import UIStyle, draw_section_header, wrap_text_label
from ..core.translations import t
from ..core.common import get_active_armature, get_all_meshes
from ..functions.eye_tracking import (
@@ -34,30 +35,29 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
layout: UILayout = self.layout
toolkit = context.scene.avatar_toolkit
# SDK Version Selection Box
sdk_box: UILayout = layout.box()
col: UILayout = sdk_box.column(align=True)
col.label(text=t("EyeTracking.sdk_version"), icon='PRESET')
col.separator(factor=0.5)
# SDK Version Selection
col = draw_section_header(layout, t("EyeTracking.sdk_version"), icon='PRESET')
row: UILayout = col.row(align=True)
row.prop(toolkit, "eye_tracking_type", expand=True)
if toolkit.eye_tracking_type == 'SDK2':
# SDK2 Warning Box
# SDK2 Warning
warning_box: UILayout = layout.box()
col: UILayout = warning_box.column(align=True)
col.label(text=t("EyeTracking.sdk2_warning"), icon='INFO')
col.separator(factor=0.5)
col.label(text=t("EyeTracking.sdk2_warning_detail1"))
col.label(text=t("EyeTracking.sdk2_warning_detail2"))
col.label(text=t("EyeTracking.sdk2_warning_detail3"))
col.label(text=t("EyeTracking.sdk2_warning_detail4"))
col.alert = True
col.label(text=t("EyeTracking.sdk2_warning"), icon='ERROR')
col.separator(factor=UIStyle.SUBSECTION_SEPARATOR_FACTOR)
# Mode Selection Box
mode_box: UILayout = layout.box()
col: UILayout = mode_box.column(align=True)
col.label(text=t("EyeTracking.setup"), icon='TOOL_SETTINGS')
col.separator(factor=0.5)
warning_text = "\n".join([
t("EyeTracking.sdk2_warning_detail1"),
t("EyeTracking.sdk2_warning_detail2"),
t("EyeTracking.sdk2_warning_detail3"),
t("EyeTracking.sdk2_warning_detail4")
])
wrap_text_label(col, warning_text, max_length=45)
# Mode Selection
col = draw_section_header(layout, t("EyeTracking.setup"), icon='TOOL_SETTINGS')
col.prop(toolkit, "eye_mode", expand=True)
if toolkit.eye_mode == 'CREATION':
@@ -72,11 +72,9 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
"""Draw the AV3 eye tracking setup interface"""
toolkit = context.scene.avatar_toolkit
# Bone Setup Box
bone_box: UILayout = layout.box()
col: UILayout = bone_box.column(align=True)
col.label(text=t("EyeTracking.bone_setup"), icon='BONE_DATA')
col.separator(factor=0.5)
# Bone Setup
col = draw_section_header(layout, t("EyeTracking.bone_setup"), icon='BONE_DATA')
armature = get_active_armature(context)
if armature:
@@ -86,21 +84,16 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
else:
col.label(text=t("EyeTracking.no_armature"), icon='ERROR')
# Create Button
row: UILayout = layout.row(align=True)
row.scale_y = 1.5
row.scale_y = UIStyle.PRIMARY_BUTTON_SCALE
row.operator(CreateEyesAV3Button.bl_idname, icon='PLAY')
def draw_creation_mode(self, context: Context, layout: UILayout) -> None:
"""Draw the eye tracking creation mode interface"""
toolkit = context.scene.avatar_toolkit
# Bone Setup Box
bone_box: UILayout = layout.box()
col: UILayout = bone_box.column(align=True)
col.label(text=t("EyeTracking.bone_setup"), icon='BONE_DATA')
col.separator(factor=0.5)
# Bone Setup
col = draw_section_header(layout, t("EyeTracking.bone_setup"), icon='BONE_DATA')
armature = get_active_armature(context)
if armature:
col.prop_search(toolkit, "head", armature.data, "bones", text=t("EyeTracking.head_bone"))
@@ -109,19 +102,12 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
else:
col.label(text=t("EyeTracking.no_armature"), icon='ERROR')
# Mesh Setup Box
mesh_box: UILayout = layout.box()
col: UILayout = mesh_box.column(align=True)
col.label(text=t("EyeTracking.mesh_setup"), icon='MESH_DATA')
col.separator(factor=0.5)
# Mesh Setup
col = draw_section_header(layout, t("EyeTracking.mesh_setup"), icon='MESH_DATA')
col.prop_search(toolkit, "mesh_name_eye", bpy.data, "objects", text="")
# Shape Key Setup Box
shape_box: UILayout = layout.box()
col: UILayout = shape_box.column(align=True)
col.label(text=t("EyeTracking.shapekey_setup"), icon='SHAPEKEY_DATA')
col.separator(factor=0.5)
# Shape Key Setup
col = draw_section_header(layout, t("EyeTracking.shapekey_setup"), icon='SHAPEKEY_DATA')
mesh = bpy.data.objects.get(toolkit.mesh_name_eye)
if mesh and mesh.data.shape_keys:
col.prop_search(toolkit, "wink_left", mesh.data.shape_keys, "key_blocks", text=t("EyeTracking.wink_left"))
@@ -131,19 +117,15 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
else:
col.label(text=t("EyeTracking.no_shapekeys"), icon='ERROR')
# Options Box
options_box: UILayout = layout.box()
col: UILayout = options_box.column(align=True)
col.label(text=t("EyeTracking.options"), icon='SETTINGS')
col.separator(factor=0.5)
# Options
col = draw_section_header(layout, t("EyeTracking.options"), icon='SETTINGS')
col.prop(toolkit, "disable_eye_blinking")
col.prop(toolkit, "disable_eye_movement")
if not toolkit.disable_eye_movement:
col.prop(toolkit, "eye_distance")
# Create Button
row: UILayout = layout.row(align=True)
row.scale_y = 1.5
row.scale_y = UIStyle.PRIMARY_BUTTON_SCALE
row.operator(CreateEyesSDK2Button.bl_idname, icon='PLAY')
def draw_testing_mode(self, context: Context, layout: UILayout) -> None:
@@ -151,37 +133,25 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
toolkit = context.scene.avatar_toolkit
if context.mode != 'POSE':
# Testing Start Box
test_box: UILayout = layout.box()
col: UILayout = test_box.column(align=True)
col.label(text=t("EyeTracking.testing"), icon='PLAY')
col.separator(factor=0.5)
# Testing Start
col = draw_section_header(layout, t("EyeTracking.testing"), icon='PLAY')
row: UILayout = col.row(align=True)
row.scale_y = 1.5
row.scale_y = UIStyle.PRIMARY_BUTTON_SCALE
row.operator(StartTestingButton.bl_idname, icon='PLAY')
else:
# Eye Rotation Box
rotation_box: UILayout = layout.box()
col: UILayout = rotation_box.column(align=True)
col.label(text=t("EyeTracking.rotation_controls"), icon='DRIVER_ROTATIONAL_DIFFERENCE')
col.separator(factor=0.5)
# Eye Rotation
col = draw_section_header(layout, t("EyeTracking.rotation_controls"), icon='DRIVER_ROTATIONAL_DIFFERENCE')
col.prop(toolkit, "eye_rotation_x", text=t("EyeTracking.rotation.x"))
col.prop(toolkit, "eye_rotation_y", text=t("EyeTracking.rotation.y"))
col.operator(ResetRotationButton.bl_idname, icon='LOOP_BACK')
# Eye Adjustment Box
adjust_box: UILayout = layout.box()
col: UILayout = adjust_box.column(align=True)
col.label(text=t("EyeTracking.adjustments"), icon='MODIFIER')
col.separator(factor=0.5)
# Eye Adjustment
col = draw_section_header(layout, t("EyeTracking.adjustments"), icon='MODIFIER')
col.prop(toolkit, "eye_distance")
col.operator(AdjustEyesButton.bl_idname, icon='CON_TRACKTO')
# Blinking Test Box
blink_box: UILayout = layout.box()
col: UILayout = blink_box.column(align=True)
col.label(text=t("EyeTracking.blink_testing"), icon='HIDE_OFF')
col.separator(factor=0.5)
# Blinking Test
col = draw_section_header(layout, t("EyeTracking.blink_testing"), icon='HIDE_OFF')
row: UILayout = col.row(align=True)
row.prop(toolkit, "eye_blink_shape")
row.operator(TestBlinking.bl_idname, icon='RESTRICT_VIEW_OFF')
@@ -192,7 +162,7 @@ class AvatarToolKit_PT_EyeTrackingPanel(Panel):
# Stop Testing Button
row: UILayout = layout.row(align=True)
row.scale_y = 1.5
row.scale_y = UIStyle.PRIMARY_BUTTON_SCALE
row.operator(StopTestingButton.bl_idname, icon='PAUSE')
# Reset Button