Files
croccybot/src/modules/skins/editor/layout.py

134 lines
4.0 KiB
Python

from typing import Optional
from dataclasses import dataclass, field
import uuid
import discord
from discord.components import SelectOption
from babel.translator import LazyStr
from gui.base.Card import Card
from utils.lib import EmbedField, tabulate
from .skinsetting import SettingInputType, Setting
from ..skinlib import CustomSkin
@dataclass
class SettingGroup:
"""
Data class representing a collection of settings which are naturally
grouped together at interface level.
Typically the settings in a single SettingGroup are displayed
in the same embed field, the settings are edited with the same modal,
and the group represents a single option in the "setting group menu".
Setting groups do not correspond to any grouping at the Card or Skin level,
and may cross multiple cards.
"""
# The name and description strings are shown in the embed field and menu option
name: LazyStr
# Tuple of settings that are part of this setting group
settings: tuple[Setting, ...]
description: Optional[LazyStr] = None
# Whether the group should be displayed in a group or not
ungrouped: bool = False
# Whether the embed field should be inline
inline: bool = True
# Component custom id to identify the editing component
# Also used as the value of the select option
custom_id: str = str(uuid.uuid4())
@property
def editable_settings(self):
return tuple(setting for setting in self.settings if setting.input_type is SettingInputType.ModalInput)
def embed_field_for(self, skin: CustomSkin) -> EmbedField:
"""
Tabulates the contained settings and builds an embed field for the editor UI.
"""
t = skin.bot.translator.t
rows: list[tuple[str, str]] = []
for setting in self.settings:
name = t(setting.display_name)
value = setting.value_in(skin) or setting.default_value_in(skin)
formatted = setting.format_value_in(skin, value)
rows.append((name, formatted))
lines = tabulate(*rows)
table = '\n'.join(lines)
description = f"*{t(self.description)}*" if self.description else ''
embed_field = EmbedField(
name=t(self.name),
value=f"{description}\n{table}",
inline=self.inline,
)
return embed_field
def select_option_for(self, skin: CustomSkin) -> SelectOption:
"""
Makes a SelectOption referring to this setting group.
"""
t = skin.bot.translator.t
option = SelectOption(
label=t(self.name),
description=t(self.description) if self.description else None,
value=self.custom_id,
)
return option
@dataclass
class Page:
"""
Represents a page of skin settings for the skin editor UI.
"""
# Various string attributes of the page
display_name: LazyStr
editing_description: Optional[LazyStr] = None
preview_description: Optional[LazyStr] = None
visible_in_preview: bool = True
render_card: Optional[type[Card]] = None
groups: list[SettingGroup] = field(default_factory=list)
def make_embed_for(self, skin: CustomSkin) -> discord.Embed:
t = skin.bot.translator.t
embed = discord.Embed(
colour=discord.Colour.orange(),
title=t(self.display_name),
)
description_lines: list[str] = []
field_counter = 0
for group in self.groups:
field = group.embed_field_for(skin)
if group.ungrouped:
description_lines.append(field.value)
else:
embed.add_field(**field._asdict())
if not (field_counter) % 3:
embed.add_field(name='', value='')
field_counter += 1
field_counter += 1
if description_lines:
embed.description = '\n'.join(description_lines)
if self.render_card is not None:
embed.set_image(url='attachment://sample.png')
return embed