Initial SE Palette add-on
This commit is contained in:
@@ -0,0 +1,148 @@
|
||||
"""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)
|
||||
Reference in New Issue
Block a user