Files
2026-09-03 03:17:27 +09:00

149 lines
4.1 KiB
Python

"""Video SequencerサイドバーのSE Palette UI。"""
from __future__ import annotations
import os
import bpy
import bpy.utils.previews as previews
from bpy.types import Panel
from .operators import populate_items
_preview_collection = None
def clear_previews():
global _preview_collection
if _preview_collection is not None:
try:
previews.remove(_preview_collection)
except Exception:
pass
_preview_collection = None
def _custom_icon_value(item) -> int:
global _preview_collection
raw_path = item.icon_path.strip()
if not raw_path:
return 0
filepath = os.path.abspath(bpy.path.abspath(raw_path))
if not os.path.isfile(filepath):
return 0
try:
if _preview_collection is None:
_preview_collection = previews.new()
key = f"{filepath}:{os.path.getmtime(filepath)}"
if key not in _preview_collection:
_preview_collection.load(key, filepath, "IMAGE")
return _preview_collection[key].icon_id
except Exception:
return 0
def _draw_insert_button(row, item):
icon_value = _custom_icon_value(item)
kwargs = {"text": item.label}
if icon_value:
kwargs["icon_value"] = icon_value
else:
kwargs["icon"] = item.icon or "PLAY_SOUND"
operator = row.operator("se_palette.insert", **kwargs)
operator.filepath = item.filepath
def draw_palette(layout, context):
"""ポップアップなど任意のUILayoutへSE Paletteを描画する。"""
props = context.scene.se_palette
folder_box = layout.box()
row = folder_box.row(align=True)
row.prop(props, "folder", text="")
row.operator("se_palette.scan", text="", icon="FILE_REFRESH")
if not props.items and os.path.isdir(os.path.abspath(bpy.path.abspath(props.folder))):
try:
populate_items(props)
except Exception:
pass
settings = layout.box()
row = settings.row(align=True)
row.prop(props, "channel")
row.prop(props, "volume")
row = settings.row(align=True)
row.prop(props, "advance_playhead")
row.prop(props, "compact")
settings.prop(props, "grid_columns", slider=True)
row = layout.row(align=True)
row.prop(props, "search", text="", icon="VIEWZOOM")
row.operator("se_palette.stop_preview", text="", icon="PAUSE")
folder = os.path.abspath(bpy.path.abspath(props.folder))
if not os.path.isdir(folder):
warning = layout.row()
warning.alert = True
warning.label(text="Select an SE folder", icon="ERROR")
return
query = props.search.strip().casefold()
visible_items = [
item
for item in props.items
if not query
or query in item.label.casefold()
or query in item.filename.casefold()
]
if not visible_items:
layout.label(
text="No matching sound effects" if query else "No sound effects found",
icon="INFO",
)
return
grid = layout.grid_flow(
row_major=True,
columns=props.grid_columns,
even_columns=True,
even_rows=True,
align=True,
)
for item in visible_items:
card = grid.box()
insert_row = card.row(align=True)
insert_row.scale_y = 1.0 if props.compact else 1.5
_draw_insert_button(insert_row, item)
controls = card.row(align=True)
preview = controls.operator(
"se_palette.preview",
text="" if props.compact else "Preview",
icon="PLAY",
)
preview.filepath = item.filepath
edit = controls.operator(
"se_palette.edit_item",
text="" if props.compact else "Edit",
icon="PREFERENCES",
)
edit.filepath = item.filepath
layout.label(text=f"{len(visible_items)} / {len(props.items)} sounds", icon="SOUND")
class SEPALETTE_PT_main(Panel):
"""VSEサイドバーに表示するSE Palette。"""
bl_label = "SE Palette"
bl_idname = "SEPALETTE_PT_main"
bl_space_type = "SEQUENCE_EDITOR"
bl_region_type = "UI"
bl_category = "SE Palette"
def draw(self, context):
draw_palette(self.layout, context)