feat(skins): Implement CustomSkin editor.

This commit is contained in:
2023-10-29 03:30:49 +02:00
parent 02068bd6b9
commit d1b9a95bd2
16 changed files with 3585 additions and 26 deletions

View File

@@ -13,7 +13,7 @@ from aiohttp import ClientSession
from data import Database from data import Database
from utils.lib import tabulate from utils.lib import tabulate
from gui.errors import RenderingException from gui.errors import RenderingException
from babel.translator import ctx_locale from babel.translator import ctx_locale, LeoBabel
from .config import Conf from .config import Conf
from .logger import logging_context, log_context, log_action_stack, log_wrap, set_logging_context from .logger import logging_context, log_context, log_action_stack, log_wrap, set_logging_context
@@ -54,9 +54,9 @@ logger = logging.getLogger(__name__)
class LionBot(Bot): class LionBot(Bot):
def __init__( def __init__(
self, *args, appname: str, shardname: str, db: Database, config: Conf, self, *args, appname: str, shardname: str, db: Database, config: Conf, translator: LeoBabel,
initial_extensions: List[str], web_client: ClientSession, app_ipc, initial_extensions: List[str], web_client: ClientSession, app_ipc,
testing_guilds: List[int] = [], translator=None, **kwargs testing_guilds: List[int] = [], **kwargs
): ):
kwargs.setdefault('tree_cls', LionTree) kwargs.setdefault('tree_cls', LionTree)
super().__init__(*args, **kwargs) super().__init__(*args, **kwargs)

View File

@@ -10,6 +10,7 @@ from frozendict import frozendict
from meta import LionCog, LionBot, LionContext from meta import LionCog, LionBot, LionContext
from meta.errors import UserInputError
from meta.logger import log_wrap from meta.logger import log_wrap
from utils.lib import MISSING, utc_now from utils.lib import MISSING, utc_now
from wards import sys_admin_ward, low_management_ward from wards import sys_admin_ward, low_management_ward
@@ -22,6 +23,7 @@ from .skinlib import appskin_as_choice, FrozenCustomSkin, CustomSkin
from .settings import GlobalSkinSettings from .settings import GlobalSkinSettings
from .settingui import GlobalSkinSettingUI from .settingui import GlobalSkinSettingUI
from .userskinui import UserSkinUI from .userskinui import UserSkinUI
from .editor.skineditor import CustomSkinEditor
_p = babel._p _p = babel._p
@@ -46,6 +48,8 @@ class CustomSkinCog(LionCog):
# Cache of custom skin id -> frozen custom skin # Cache of custom skin id -> frozen custom skin
self.custom_skins: LRUCache[int, FrozenCustomSkin] = LRUCache(maxsize=1000) self.custom_skins: LRUCache[int, FrozenCustomSkin] = LRUCache(maxsize=1000)
self.current_default: Optional[str] = None
async def cog_load(self): async def cog_load(self):
await self.data.init() await self.data.init()
@@ -61,6 +65,7 @@ class CustomSkinCog(LionCog):
await self._reload_appskins() await self._reload_appskins()
await self._reload_property_map() await self._reload_property_map()
await self.get_default_skin()
async def _reload_property_map(self): async def _reload_property_map(self):
""" """
@@ -123,6 +128,7 @@ class CustomSkinCog(LionCog):
""" """
setting = self.bot_settings.DefaultSkin setting = self.bot_settings.DefaultSkin
instance = await setting.get(self.bot.appname) instance = await setting.get(self.bot.appname)
self.current_default = instance.value
return instance.value return instance.value
async def fetch_property_ids(self, *card_properties: tuple[str, str]) -> list[int]: async def fetch_property_ids(self, *card_properties: tuple[str, str]) -> list[int]:
@@ -203,7 +209,7 @@ class CustomSkinCog(LionCog):
skin_args = await self.args_for_skin(skinid, card_id) skin_args = await self.args_for_skin(skinid, card_id)
args.update(skin_args) args.update(skin_args)
default = await self.get_default_skin() default = self.current_default
if default: if default:
args.setdefault("base_skin_id", default) args.setdefault("base_skin_id", default)
@@ -224,11 +230,20 @@ class CustomSkinCog(LionCog):
Update cached args for given custom skin id. Update cached args for given custom skin id.
""" """
self.custom_skins.pop(skinid, None) self.custom_skins.pop(skinid, None)
custom_skin = await CustomSkin.fetch(skinid) custom_skin = await CustomSkin.fetch(self.bot, skinid)
if custom_skin is not None: if custom_skin is not None:
skin = custom_skin.freeze() skin = custom_skin.freeze()
self.custom_skins[skinid] = skin self.custom_skins[skinid] = skin
@LionCog.listener('on_botset_skin')
async def handle_botset_skin(self, appname, instance):
await self.bot.global_dispatch('global_botset_skin', appname)
@LionCog.listener('on_global_botset_skin')
async def refresh_default_skin(self, appname):
await self.bot.core.data.BotConfig.fetch(appname, cached=False)
await self.get_default_skin()
# ----- Userspace commands ----- # ----- Userspace commands -----
@LionCog.placeholder_group @LionCog.placeholder_group
@cmds.hybrid_group("my", with_app_command=False) @cmds.hybrid_group("my", with_app_command=False)
@@ -268,8 +283,40 @@ class CustomSkinCog(LionCog):
return return
if not ctx.guild: if not ctx.guild:
return return
# TODO t = self.bot.translator.t
...
# Check guild premium status
premiumcog = self.bot.get_cog('PremiumCog')
guild_row = await premiumcog.data.PremiumGuild.fetch(ctx.guild.id, cached=False)
if not guild_row:
raise UserInputError(
t(_p(
'cmd:admin_brand|error:not_premium',
"Only premium servers can modify their interface theme! "
"Use the {premium} command to upgrade your server."
)).format(premium=self.bot.core.mention_cmd('premium'))
)
await ctx.interaction.response.defer(thinking=True, ephemeral=False)
if guild_row.custom_skin_id is None:
# Create new custom skin
skin_data = await self.data.CustomisedSkin.create(
base_skin_id=self.appskin_names.inverse[self.current_default] if self.current_default else None
)
await guild_row.update(custom_skin_id=skin_data.custom_skin_id)
skinid = guild_row.custom_skin_id
custom_skin = await CustomSkin.fetch(self.bot, skinid)
if custom_skin is None:
raise ValueError("Invalid custom skin id")
# Open the CustomSkinEditor with this skin
ui = CustomSkinEditor(custom_skin, callerid=ctx.author.id)
await ui.send(ctx.channel)
await ctx.interaction.delete_original_response()
await ui.wait()
# ----- Owner commands ----- # ----- Owner commands -----
@LionCog.placeholder_group @LionCog.placeholder_group

View File

@@ -0,0 +1,133 @@
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

View File

@@ -0,0 +1,16 @@
from .stats import stats_page
from .profile import profile_page
from .summary import summary_page
from .weekly import weekly_page
from .monthly import monthly_page
from .weekly_goals import weekly_goal_page
from .monthly_goals import monthly_goal_page
from .leaderboard import leaderboard_page
pages = [
profile_page, stats_page,
weekly_page, monthly_page,
weekly_goal_page, monthly_goal_page,
leaderboard_page,
]

View File

@@ -0,0 +1,294 @@
from gui.cards import LeaderboardCard
from ... import babel
from ..skinsetting import ColourSetting, SkinSetting, ColoursSetting
from ..layout import Page, SettingGroup
_p = babel._p
"""
top_position_colour
top_name_colour
top_hours_colour
entry_position_colour
entry_position_highlight_colour
entry_name_colour
entry_hours_colour
header_text_colour
[subheader_name_colour, subheader_value_colour]
entry_bg_colour
entry_bg_highlight_colour
"""
leaderboard_page = Page(
display_name=_p('skinsettings|page:leaderboard|display_name', "Leaderboard"),
editing_description=_p(
'skinsettings|page:leaderboard|edit_desc',
"Options for the Leaderboard pages."
),
preview_description=None,
visible_in_preview=True,
render_card=LeaderboardCard
)
header_text_colour = ColourSetting(
card=LeaderboardCard,
property_name='header_text_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:header_text_colour|display_name',
"Header"
),
description=_p(
'skinsettings|page:leaderboard|set:header_text_colour|desc',
"Text colour of the leaderboard header."
)
)
subheader_name_colour = ColourSetting(
card=LeaderboardCard,
property_name='subheader_name_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:subheader_name_colour|display_name',
""
),
description=_p(
'skinsettings|page:leaderboard|set:subheader_name_colour|desc',
""
)
)
subheader_value_colour = ColourSetting(
card=LeaderboardCard,
property_name='subheader_value_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:subheader_value_colour|display_name',
""
),
description=_p(
'skinsettings|page:leaderboard|set:subheader_value_colour|desc',
""
)
)
subheader_colour = ColoursSetting(
subheader_value_colour,
subheader_name_colour,
display_name=_p(
'skinsettings|page:leaderboard|set:subheader_colour|display_name',
"Sub-Header"
),
description=_p(
'skinsettings|page:leaderboard|set:subheader_colour|desc',
"Text colour of the sub-header line."
)
)
top_position_colour = ColourSetting(
card=LeaderboardCard,
property_name='top_position_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:top_position_colour|display_name',
"Position"
),
description=_p(
'skinsettings|page:leaderboard|set:top_position_colour|desc',
"Top 3 position colour."
)
)
top_name_colour = ColourSetting(
card=LeaderboardCard,
property_name='top_name_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:top_name_colour|display_name',
"Name"
),
description=_p(
'skinsettings|page:leaderboard|set:top_name_colour|desc',
"Top 3 name colour."
)
)
top_hours_colour = ColourSetting(
card=LeaderboardCard,
property_name='top_hours_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:top_hours_colour|display_name',
"Hours"
),
description=_p(
'skinsettings|page:leaderboard|set:top_hours_colour|desc',
"Top 3 hours colour."
)
)
entry_position_colour = ColourSetting(
card=LeaderboardCard,
property_name='entry_position_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:entry_position_colour|display_name',
"Position"
),
description=_p(
'skinsettings|page:leaderboard|set:entry_position_colour|desc',
"Position text colour."
)
)
entry_position_highlight_colour = ColourSetting(
card=LeaderboardCard,
property_name='entry_position_highlight_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:entry_position_highlight_colour|display_name',
"Position (HL)"
),
description=_p(
'skinsettings|page:leaderboard|set:entry_position_highlight_colour|desc',
"Highlighted position colour."
)
)
entry_name_colour = ColourSetting(
card=LeaderboardCard,
property_name='entry_name_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:entry_name_colour|display_name',
"Name"
),
description=_p(
'skinsettings|page:leaderboard|set:entry_name_colour|desc',
"Entry name colour."
)
)
entry_hours_colour = ColourSetting(
card=LeaderboardCard,
property_name='entry_hours_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:entry_hours_colour|display_name',
"Hours"
),
description=_p(
'skinsettings|page:leaderboard|set:entry_hours_colour|desc',
"Entry hours colour."
)
)
entry_bg_colour = ColourSetting(
card=LeaderboardCard,
property_name='entry_bg_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:entry_bg_colour|display_name',
"Regular"
),
description=_p(
'skinsettings|page:leaderboard|set:entry_bg_colour|desc',
"Background colour of regular entries."
)
)
entry_bg_highlight_colour = ColourSetting(
card=LeaderboardCard,
property_name='entry_bg_highlight_colour',
display_name=_p(
'skinsettings|page:leaderboard|set:entry_bg_highlight_colour|display_name',
"Highlighted"
),
description=_p(
'skinsettings|page:leaderboard|set:entry_bg_highlight_colour|desc',
"Background colour of highlighted entries."
)
)
top_colour_group = SettingGroup(
_p('skinsettings|page:leaderboard|grp:top_colour', "Top 3"),
description=_p(
'skinsettings|page:leaderboard|grp:top_colour|desc',
"Customise the text colours for the top 3 positions."
),
custom_id='leaderboard-top',
settings=(
top_position_colour,
top_name_colour,
top_hours_colour
)
)
entry_text_group = SettingGroup(
_p('skinsettings|page:leaderboard|grp:entry_text', "Entry Text"),
description=_p(
'skinsettings|page:leaderboard|grp:entry_text|desc',
"Text colours of the leaderboard entries."
),
custom_id='leaderboard-text',
settings=(
entry_position_colour,
entry_position_highlight_colour,
entry_name_colour,
entry_hours_colour
)
)
entry_bg_group = SettingGroup(
_p('skinsettings|page:leaderboard|grp:entry_bg', "Entry Background"),
description=_p(
'skinsettings|page:leaderboard|grp:entry_bg|desc',
"Background colours of the leaderboard entries."
),
custom_id='leaderboard-bg',
settings=(
entry_bg_colour,
entry_bg_highlight_colour
)
)
misc_group = SettingGroup(
_p('skinsettings|page:leaderboard|grp:misc', "Miscellaneous"),
description=_p(
'skinsettings|page:leaderboard|grp:misc|desc',
"Other miscellaneous colour settings."
),
custom_id='leaderboard-misc',
settings=(
header_text_colour,
subheader_colour
)
)
base_skin = SkinSetting(
card=LeaderboardCard,
property_name='base_skin_id',
display_name=_p(
'skinsettings|page:leaderboard|set:base_skin|display_name',
'Skin'
),
description=_p(
'skinsettings|page:leaderboard|set:base_skin|desc',
"Select a Skin Preset."
)
)
base_skin_group = SettingGroup(
_p('skinsettings|page:leaderboard|grp:base_skin', "Leaderboard Skin"),
description=_p(
'skinsettings|page:leaderboard|grp:base_skin|desc',
"Asset pack and default values for the Leaderboard."
),
custom_id='leaderboard-skin',
settings=(base_skin,),
ungrouped=True
)
leaderboard_page.groups = [
base_skin_group,
top_colour_group,
entry_text_group,
entry_bg_group,
misc_group
]

View File

@@ -0,0 +1,376 @@
from gui.cards import MonthlyStatsCard
from ... import babel
from ..skinsetting import ColourSetting, SkinSetting, ColoursSetting
from ..layout import Page, SettingGroup
_p = babel._p
""""
title_colour
[this_month_colour, last_month_colour]
[stats_key_colour, stats_value_colour]
footer_colour
top_hours_colour
top_hours_bg_colour
top_date_colour
top_line_colour
top_this_colour
top_this_hours_colour
top_last_colour
top_last_hours_colour
weekday_background_colour
weekday_colour
month_background_colour
month_colour
"""
monthly_page = Page(
display_name=_p('skinsettings|page:monthly|display_name', "Monthly Statistics"),
editing_description=_p(
'skinsettings|page:monthly|edit_desc',
"Options for the monthly statistis card."
),
preview_description=None,
visible_in_preview=True,
render_card=MonthlyStatsCard
)
title_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='title_colour',
display_name=_p(
'skinsettings|page:monthly|set:title_colour|display_name',
'Title'
),
description=_p(
'skinsettings|page:monthly|set:title_colour|desc',
"Colour of the card title."
)
)
top_hours_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='top_hours_colour',
display_name=_p(
'skinsettings|page:monthly|set:top_hours_colour|display_name',
'Hours'
),
description=_p(
'skinsettings|page:monthly|set:top_hours_colour|desc',
"Hour axis labels."
)
)
top_hours_bg_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='top_hours_bg_colour',
display_name=_p(
'skinsettings|page:monthly|set:top_hours_bg_colour|display_name',
'Hours Bg'
),
description=_p(
'skinsettings|page:monthly|set:top_hours_bg_colour|desc',
"Hour axis label background."
)
)
top_line_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='top_line_colour',
display_name=_p(
'skinsettings|page:monthly|set:top_line_colour|display_name',
'Lines'
),
description=_p(
'skinsettings|page:monthly|set:top_line_colour|desc',
"Horizontal graph lines."
)
)
top_date_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='top_date_colour',
display_name=_p(
'skinsettings|page:monthly|set:top_date_colour|display_name',
'Days'
),
description=_p(
'skinsettings|page:monthly|set:top_date_colour|desc',
"Day axis labels."
)
)
top_this_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='top_this_colour',
display_name=_p(
'skinsettings|page:monthly|set:top_this_colour|display_name',
'This Month Bar'
),
description=_p(
'skinsettings|page:monthly|set:top_this_colour|desc',
"This month bar fill colour."
)
)
top_last_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='top_last_colour',
display_name=_p(
'skinsettings|page:monthly|set:top_last_colour|display_name',
'Last Month Bar'
),
description=_p(
'skinsettings|page:monthly|set:top_last_colour|desc',
"Last month bar fill colour."
)
)
top_this_hours_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='top_this_hours_colour',
display_name=_p(
'skinsettings|page:monthly|set:top_this_hours_colour|display_name',
'This Month Hours'
),
description=_p(
'skinsettings|page:monthly|set:top_this_hours_colour|desc',
"This month hour text."
)
)
top_last_hours_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='top_last_hours_colour',
display_name=_p(
'skinsettings|page:monthly|set:top_last_hours_colour|display_name',
'Last Month Hours'
),
description=_p(
'skinsettings|page:monthly|set:top_last_hours_colour|desc',
"Last month hour text."
)
)
this_month_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='this_month_colour',
display_name=_p(
'skinsettings|page:monthly|set:this_month_colour|display_name',
'This Month Legend'
),
description=_p(
'skinsettings|page:monthly|set:this_month_colour|desc',
"This month legend text."
)
)
last_month_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='last_month_colour',
display_name=_p(
'skinsettings|page:monthly|set:last_month_colour|display_name',
'Last Month Legend'
),
description=_p(
'skinsettings|page:monthly|set:last_month_colour|desc',
"Last month legend text."
)
)
legend_colour = ColoursSetting(
this_month_colour,
last_month_colour,
display_name=_p(
'skinsettings|page:monthly|set:legend_colour|display_name',
'Legend'
),
description=_p(
'skinsettings|page:monthly|set:legend_colour|desc',
"Legend text colour."
)
)
weekday_background_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='weekday_background_colour',
display_name=_p(
'skinsettings|page:monthly|set:weekday_background_colour|display_name',
'Weekday Bg'
),
description=_p(
'skinsettings|page:monthly|set:weekday_background_colour|desc',
"Weekday axis background colour."
)
)
weekday_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='weekday_colour',
display_name=_p(
'skinsettings|page:monthly|set:weekday_colour|display_name',
'Weekdays'
),
description=_p(
'skinsettings|page:monthly|set:weekday_colour|desc',
"Weekday axis labels."
)
)
month_background_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='month_background_colour',
display_name=_p(
'skinsettings|page:monthly|set:month_background_colour|display_name',
'Month Bg'
),
description=_p(
'skinsettings|page:monthly|set:month_background_colour|desc',
"Month axis label backgrounds."
)
)
month_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='month_colour',
display_name=_p(
'skinsettings|page:monthly|set:month_colour|display_name',
'Months'
),
description=_p(
'skinsettings|page:monthly|set:month_colour|desc',
"Month axis label text."
)
)
stats_key_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='stats_key_colour',
display_name=_p(
'skinsettings|page:monthly|set:stats_key_colour|display_name',
'Stat Names'
),
description=_p(
'skinsettings|page:monthly|set:stats_key_colour|desc',
""
)
)
stats_value_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='stats_value_colour',
display_name=_p(
'skinsettings|page:monthly|set:stats_value_colour|display_name',
'Stat Values'
),
description=_p(
'skinsettings|page:monthly|set:stats_value_colour|desc',
""
)
)
statistics_colour = ColoursSetting(
stats_key_colour,
stats_value_colour,
display_name=_p(
'skinsettings|page:monthly|set:statistics_colour|display_name',
'Statistics'
),
description=_p(
'skinsettings|page:monthly|set:statistics_colour|desc',
"Summary Statistics"
)
)
footer_colour = ColourSetting(
card=MonthlyStatsCard,
property_name='footer_colour',
display_name=_p(
'skinsettings|page:monthly|set:footer_colour|display_name',
'Footer'
),
description=_p(
'skinsettings|page:monthly|set:footer_colour|desc',
"Footer text colour."
)
)
top_graph_group = SettingGroup(
_p('skinsettings|page:monthly|grp:top_graph', "Top Graph"),
description=_p(
'skinsettings|page:monthly|grp:top_graph|desc',
"Customise the axis and style of the top graph."
),
custom_id='monthly-top',
settings=(
top_hours_colour,
top_hours_bg_colour,
top_date_colour,
top_line_colour
)
)
top_hours_group = SettingGroup(
_p('skinsettings|page:monthly|grp:top_hours', "Hours"),
description=_p(
'skinsettings|page:monthly|grp:top_hours|desc',
"Customise the colour of this week and last week."
),
custom_id='monthly-hours',
settings=(
top_this_colour,
top_this_hours_colour,
top_last_colour,
top_last_hours_colour
)
)
bottom_graph_group = SettingGroup(
_p('skinsettings|page:monthly|grp:bottom_graph', "Heatmap"),
description=_p(
'skinsettings|page:monthly|grp:bottom_graph|desc',
"Customise the axis and style of the heatmap."
),
custom_id='monthly-heatmap',
settings=(
weekday_background_colour,
weekday_colour,
month_background_colour,
month_colour
)
)
misc_group = SettingGroup(
_p('skinsettings|page:monthly|grp:misc', "Miscellaneous"),
description=_p(
'skinsettings|page:monthly|grp:misc|desc',
"Miscellaneous colour options."
),
custom_id='monthly-misc',
settings=(
title_colour,
legend_colour,
statistics_colour,
footer_colour
)
)
base_skin = SkinSetting(
card=MonthlyStatsCard,
property_name='base_skin_id',
display_name=_p(
'skinsettings|page:monthly|set:base_skin|display_name',
'Skin'
),
description=_p(
'skinsettings|page:monthly|set:base_skin|desc',
"Select a Skin Preset."
)
)
base_skin_group = SettingGroup(
_p('skinsettings|page:monthly|grp:base_skin', "Monthly Stats Skin"),
description=_p(
'skinsettings|page:monthly|grp:base_skin|desc',
"Asset pack and default values for the Monthly Statistics."
),
custom_id='monthly-skin',
settings=(base_skin,),
ungrouped=True
)
monthly_page.groups = [
base_skin_group,
top_graph_group,
top_hours_group,
bottom_graph_group,
misc_group
]

View File

@@ -0,0 +1,436 @@
from gui.cards import MonthlyGoalCard
from ... import babel
from ..skinsetting import ColourSetting, SkinSetting, ColoursSetting
from ..layout import Page, SettingGroup
_p = babel._p
"""
mini_profile_name_colour
mini_profile_badge_colour
mini_profile_badge_text_colour
mini_profile_discrim_colour
title_colour
footer_colour
progress_bg_colour
progress_colour
[attendance_rate_colour, task_count_colour, studied_hour_colour]
[attendance_colour, task_done_colour, studied_text_colour, task_goal_colour]
task_goal_number_colour
task_header_colour
task_done_number_colour
task_undone_number_colour
task_done_text_colour
task_undone_text_colour
"""
monthly_goal_page = Page(
display_name=_p('skinsettings|page:monthly_goal|display_name', "Monthly Goals"),
editing_description=_p(
'skinsettings|page:monthly_goal|edit_desc',
"Options for the monthly goal card."
),
preview_description=None,
visible_in_preview=True,
render_card=MonthlyGoalCard
)
title_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='title_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:title_colour|display_name',
"Title"
),
description=_p(
'skinsettings|page:monthly_goal|set:title_colour|desc',
""
)
)
progress_bg_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='progress_bg_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:progress_bg_colour|display_name',
"Bar Bg"
),
description=_p(
'skinsettings|page:monthly_goal|set:progress_bg_colour|desc',
""
)
)
progress_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='progress_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:progress_colour|display_name',
"Bar Fg"
),
description=_p(
'skinsettings|page:monthly_goal|set:progress_colour|desc',
""
)
)
attendance_rate_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='attendance_rate_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:attendance_rate_colour|display_name',
""
),
description=_p(
'skinsettings|page:monthly_goal|set:attendance_rate_colour|desc',
""
)
)
attendance_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='attendance_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:attendance_colour|display_name',
""
),
description=_p(
'skinsettings|page:monthly_goal|set:attendance_colour|desc',
""
)
)
task_count_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='task_count_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:task_count_colour|display_name',
""
),
description=_p(
'skinsettings|page:monthly_goal|set:task_count_colour|desc',
""
)
)
task_done_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='task_done_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:task_done_colour|display_name',
""
),
description=_p(
'skinsettings|page:monthly_goal|set:task_done_colour|desc',
""
)
)
task_goal_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='task_goal_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:task_goal_colour|display_name',
""
),
description=_p(
'skinsettings|page:monthly_goal|set:task_goal_colour|desc',
""
)
)
task_goal_number_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='task_goal_number_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:task_goal_number_colour|display_name',
"Task Goal"
),
description=_p(
'skinsettings|page:monthly_goal|set:task_goal_number_colour|desc',
""
)
)
studied_text_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='studied_text_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:studied_text_colour|display_name',
""
),
description=_p(
'skinsettings|page:monthly_goal|set:studied_text_colour|desc',
""
)
)
studied_hour_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='studied_hour_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:studied_hour_colour|display_name',
""
),
description=_p(
'skinsettings|page:monthly_goal|set:studied_hour_colour|desc',
""
)
)
text_highlight_colour = ColoursSetting(
attendance_rate_colour,
task_count_colour,
studied_hour_colour,
display_name=_p(
'skinsettings|page:monthly_goal|set:text_highlight_colour|display_name',
"Highlight Text"
),
description=_p(
'skinsettings|page:monthly_goal|set:text_highlight_colour|desc',
"Progress text colour."
)
)
text_colour = ColoursSetting(
attendance_colour,
task_done_colour,
studied_text_colour,
task_goal_colour,
display_name=_p(
'skinsettings|page:monthly_goal|set:text_colour|display_name',
"Text"
),
description=_p(
'skinsettings|page:monthly_goal|set:text_colour|desc',
"Achievement description text colour."
)
)
task_header_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='task_header_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:task_header_colour|display_name',
"Task Header"
),
description=_p(
'skinsettings|page:monthly_goal|set:task_header_colour|desc',
""
)
)
task_done_number_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='task_done_number_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:task_done_number_colour|display_name',
"Checked Number"
),
description=_p(
'skinsettings|page:monthly_goal|set:task_done_number_colour|desc',
""
)
)
task_undone_number_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='task_undone_number_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:task_undone_number_colour|display_name',
"Unchecked Number"
),
description=_p(
'skinsettings|page:monthly_goal|set:task_undone_number_colour|desc',
""
)
)
task_done_text_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='task_done_text_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:task_done_text_colour|display_name',
"Checked Text"
),
description=_p(
'skinsettings|page:monthly_goal|set:task_done_text_colour|desc',
""
)
)
task_undone_text_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='task_undone_text_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:task_undone_text_colour|display_name',
"Unchecked Text"
),
description=_p(
'skinsettings|page:monthly_goal|set:task_undone_text_colour|desc',
""
)
)
footer_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='footer_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:footer_colour|display_name',
"Footer"
),
description=_p(
'skinsettings|page:monthly_goal|set:footer_colour|desc',
""
)
)
mini_profile_badge_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='mini_profile_badge_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:mini_profile_badge_colour|display_name',
'Badge Background'
),
description=_p(
'skinsettings|page:monthly_goal|set:mini_profile_badge_colour|desc',
"Mini-profile badge background colour."
)
)
mini_profile_badge_text_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='mini_profile_badge_text_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:mini_profile_badge_text_colour|display_name',
'Badge Text'
),
description=_p(
'skinsettings|page:monthly_goal|set:mini_profile_badge_text_colour|desc',
""
)
)
mini_profile_name_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='mini_profile_name_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:mini_profile_name_colour|display_name',
'Username'
),
description=_p(
'skinsettings|page:monthly_goal|set:mini_profile_name_colour|desc',
"Mini-profile username colour."
)
)
mini_profile_discrim_colour = ColourSetting(
card=MonthlyGoalCard,
property_name='mini_profile_discrim_colour',
display_name=_p(
'skinsettings|page:monthly_goal|set:mini_profile_discrim_colour|display_name',
'Discriminator'
),
description=_p(
'skinsettings|page:monthly_goal|set:mini_profile_discrim_colour|desc',
"Mini-profile discriminator colour."
)
)
mini_profile_group = SettingGroup(
_p('skinsettings|page:monthly_goal|grp:mini_profile', "Profile"),
description=_p(
'skinsettings|page:monthly_goal|grp:mini_profile|desc',
"Customise the mini-profile shown above the goals."
),
custom_id='monthlygoal-profile',
settings=(
mini_profile_name_colour,
mini_profile_discrim_colour,
mini_profile_badge_colour,
mini_profile_badge_text_colour,
)
)
misc_group = SettingGroup(
_p('skinsettings|page:monthly_goal|grp:misc', "Miscellaneous"),
description=_p(
'skinsettings|page:monthly_goal|grp:misc|desc',
"Other miscellaneous colours."
),
custom_id='monthlygoal-misc',
settings=(
title_colour,
footer_colour,
)
)
task_colour_group = SettingGroup(
_p('skinsettings|page:monthly_goal|grp:task_colour', "Task colours"),
description=_p(
'skinsettings|page:monthly_goal|grp:task_colour|desc',
"Text and number colours for (in)complete goals."
),
custom_id='monthlygoal-tasks',
settings=(
task_undone_number_colour,
task_done_number_colour,
task_undone_text_colour,
task_done_text_colour,
)
)
progress_colour_group = SettingGroup(
_p('skinsettings|page:monthly_goal|grp:progress_colour', "Progress Colours"),
description=_p(
'skinsettings|page:monthly_goal|grp:progress_colour|desc',
"Customise colours for the monthly achievement progress."
),
custom_id='monthlygoal-progress',
settings=(
progress_bg_colour,
progress_colour,
text_colour,
text_highlight_colour,
task_goal_number_colour
)
)
base_skin = SkinSetting(
card=MonthlyGoalCard,
property_name='base_skin_id',
display_name=_p(
'skinsettings|page:monthly_goal|set:base_skin|display_name',
'Skin'
),
description=_p(
'skinsettings|page:monthly_goal|set:base_skin|desc',
"Select a Skin Preset."
)
)
base_skin_group = SettingGroup(
_p('skinsettings|page:monthly_goal|grp:base_skin', "Monthly Goals Skin"),
description=_p(
'skinsettings|page:monthly_goal|grp:base_skin|desc',
"Asset pack and default values for the Monthly Goals."
),
custom_id='monthlygoals-skin',
settings=(base_skin,),
ungrouped=True
)
monthly_goal_page.groups = [
base_skin_group,
mini_profile_group,
misc_group,
progress_colour_group,
task_colour_group
]

View File

@@ -0,0 +1,266 @@
from gui.cards import ProfileCard
from ... import babel
from ..skinsetting import ColourSetting, SkinSetting, ColoursSetting
from ..layout import Page, SettingGroup
_p = babel._p
profile_page = Page(
display_name=_p('skinsettings|page:profile|display_name', "Member Profile"),
editing_description=_p(
'skinsettings|page:profile|edit_desc',
"Options for the member profile card."
),
preview_description=None,
visible_in_preview=True,
render_card=ProfileCard
)
header_colour_1 = ColourSetting(
card=ProfileCard,
property_name='header_colour_1',
display_name=_p(
'skinsettings|page:profile|set:header_colour_1|display_name',
'Username'
),
description=_p(
'skinsettings|page:profile|set:header_colour_1|desc',
"Text colour of the profile username."
)
)
header_colour_2 = ColourSetting(
card=ProfileCard,
property_name='header_colour_2',
display_name=_p(
'skinsettings|page:profile|set:header_colour_2|display_name',
'Discriminator'
),
description=_p(
'skinsettings|page:profile|set:header_colour_2|desc',
"Text colour of the profile dscriminator."
)
)
counter_bg_colour = ColourSetting(
card=ProfileCard,
property_name='counter_bg_colour',
display_name=_p(
'skinsettings|page:profile|set:counter_bg_colour|display_name',
'Background'
),
description=_p(
'skinsettings|page:profile|set:counter_bg_colour|desc',
"Colour of the coin/gem/gift backgrounds."
)
)
counter_colour = ColourSetting(
card=ProfileCard,
property_name='counter_colour',
display_name=_p(
'skinsettings|page:profile|set:counter_colour|display_name',
'Text'
),
description=_p(
'skinsettings|page:profile|set:counter_colour|desc',
"Colour of the coin/gem/gift text."
)
)
subheader_colour = ColourSetting(
card=ProfileCard,
property_name='subheader_colour',
display_name=_p(
'skinsettings|page:profile|set:subheader_colour|display_name',
'Column Header'
),
description=_p(
'skinsettings|page:profile|set:subheader_colour|desc',
"Colour of the Profile/Achievements header."
)
)
badge_text_colour = ColourSetting(
card=ProfileCard,
property_name='badge_text_colour',
display_name=_p(
'skinsettings|page:profile|set:badge_text_colour|display_name',
'Badge Text'
),
description=_p(
'skinsettings|page:profile|set:badge_text_colour|desc',
"Colour of the profile badge text."
)
)
badge_blob_colour = ColourSetting(
card=ProfileCard,
property_name='badge_blob_colour',
display_name=_p(
'skinsettings|page:profile|set:badge_blob_colour|display_name',
'Background'
),
description=_p(
'skinsettings|page:profile|set:badge_blob_colour|desc',
"Colour of the profile badge background."
)
)
rank_name_colour = ColourSetting(
card=ProfileCard,
property_name='rank_name_colour',
display_name=_p(
'skinsettings|page:profile|set:rank_name_colour|display_name',
'Current Rank'
),
description=_p(
'skinsettings|page:profile|set:rank_name_colour|desc',
"Colour of the current study rank name."
)
)
rank_hours_colour = ColourSetting(
card=ProfileCard,
property_name='rank_hours_colour',
display_name=_p(
'skinsettings|page:profile|set:rank_hours_colour|display_name',
'Required Hours'
),
description=_p(
'skinsettings|page:profile|set:rank_hours_colour|desc',
"Colour of the study rank hour range."
)
)
bar_full_colour = ColourSetting(
card=ProfileCard,
property_name='bar_full_colour',
display_name=_p(
'skinsettings|page:profile|set:bar_full_colour|display_name',
'Bar Full'
),
description=_p(
'skinsettings|page:profile|set:bar_full_colour|desc',
"Foreground progress bar colour."
)
)
bar_empty_colour = ColourSetting(
card=ProfileCard,
property_name='bar_empty_colour',
display_name=_p(
'skinsettings|page:profile|set:bar_empty_colour|display_name',
'Bar Empty'
),
description=_p(
'skinsettings|page:profile|set:bar_empty_colour|desc',
"Background progress bar colour."
)
)
next_rank_colour = ColourSetting(
card=ProfileCard,
property_name='next_rank_colour',
display_name=_p(
'skinsettings|page:profile|set:next_rank_colour|display_name',
'Next Rank'
),
description=_p(
'skinsettings|page:profile|set:next_rank_colour|desc',
"Colour of the next rank name and hours."
)
)
title_colour_group = SettingGroup(
_p('skinsettings|page:profile|grp:title_colour', "Title Colours"),
description=_p(
'skinsettings|page:profile|grp:title_colour|desc',
"Header and suheader text colours."
),
custom_id='profile-titles',
settings=(
header_colour_1,
header_colour_2,
subheader_colour
),
)
badge_colour_group = SettingGroup(
_p('skinsettings|page:profile|grp:badge_colour', "Profile Badge Colours"),
description=_p(
'skinsettings|page:profile|grp:badge_colour|desc',
"Text and background for the profile badges."
),
custom_id='profile-badges',
settings=(
badge_text_colour,
badge_blob_colour
),
)
counter_colour_group = SettingGroup(
_p('skinsettings|page:profile|grp:counter_colour', "Counter Colours"),
description=_p(
'skinsettings|page:profile|grp:counter_colour|desc',
"Text and background for the coin/gem/gift counters."
),
custom_id='profile-counters',
settings=(
counter_colour,
counter_bg_colour
),
)
rank_colour_group = SettingGroup(
_p('skinsettings|page:profile|grp:rank_colour', "Progress Bar"),
description=_p(
'skinsettings|page:profile|grp:rank_colour|desc',
"Colours for the study badge/rank progress bar."
),
custom_id='profile-progress',
settings=(
rank_name_colour,
rank_hours_colour,
next_rank_colour,
bar_full_colour,
bar_empty_colour
),
)
base_skin = SkinSetting(
card=ProfileCard,
property_name='base_skin_id',
display_name=_p(
'skinsettings|page:profile|set:base_skin|display_name',
'Skin'
),
description=_p(
'skinsettings|page:profile|set:base_skin|desc',
"Select a Skin Preset."
)
)
base_skin_group = SettingGroup(
_p('skinsettings|page:profile|grp:base_skin', "Profile Skin"),
description=_p(
'skinsettings|page:profile|grp:base_skin|desc',
"Asset pack and default values for this card."
),
custom_id='profile-skin',
settings=(base_skin,),
ungrouped=True
)
profile_page.groups = [
base_skin_group,
title_colour_group,
badge_colour_group,
rank_colour_group,
counter_colour_group,
]

View File

@@ -0,0 +1,211 @@
from gui.cards import StatsCard
from ... import babel
from ..skinsetting import ColourSetting, SkinSetting, ColoursSetting
from ..layout import Page, SettingGroup
_p = babel._p
stats_page = Page(
display_name=_p('skinsettings|page:stats|display_name', "Statistics"),
editing_description=_p(
'skinsettings|page:stats|edit_desc',
"Options for the member statistics card."
),
preview_description=None,
visible_in_preview=True,
render_card=StatsCard
)
header_colour = ColourSetting(
card=StatsCard,
property_name='header_colour',
display_name=_p(
'skinsettings|page:stats|set:header_colour|display_name',
'Titles'
),
description=_p(
'skinsettings|page:stats|set:header_colour|desc',
"Top header text colour."
)
)
stats_subheader_colour = ColourSetting(
card=StatsCard,
property_name='stats_subheader_colour',
display_name=_p(
'skinsettings|page:stats|set:stats_subheader_colour|display_name',
'Sections'
),
description=_p(
'skinsettings|page:stats|set:stats_subheader_colour|desc',
"Text colour of the Statistics section titles."
)
)
stats_text_colour = ColourSetting(
card=StatsCard,
property_name='stats_text_colour',
display_name=_p(
'skinsettings|page:stats|set:stats_text_colour|display_name',
'Statistics'
),
description=_p(
'skinsettings|page:stats|set:stats_text_colour|desc',
"Text colour of the Statistics section bodies."
)
)
col2_date_colour = ColourSetting(
card=StatsCard,
property_name='col2_date_colour',
display_name=_p(
'skinsettings|page:stats|set:col2_date_colour|display_name',
'Date'
),
description=_p(
'skinsettings|page:stats|set:col2_date_colour|desc',
"Colour of the current month and year."
)
)
col2_hours_colour = ColourSetting(
card=StatsCard,
property_name='col2_hours_colour',
display_name=_p(
'skinsettings|page:stats|set:col2_hours_colour|display_name',
'Hours'
),
description=_p(
'skinsettings|page:stats|set:col2_hours_colour|desc',
"Colour of the monthly hour total."
)
)
text_colour_group = SettingGroup(
_p('skinsettings|page:stats|grp:text_colour', "Text Colours"),
description=_p(
'skinsettings|page:stats|grp:text_colour|desc',
"Header colours and statistics text colours."
),
custom_id='stats-text',
settings=(
header_colour,
stats_subheader_colour,
stats_text_colour,
col2_date_colour,
col2_hours_colour
)
)
cal_weekday_colour = ColourSetting(
card=StatsCard,
property_name='cal_weekday_colour',
display_name=_p(
'skinsettings|page:stats|set:cal_weekday_colour|display_name',
'Weekdays'
),
description=_p(
'skinsettings|page:stats|set:cal_weekday_colour|desc',
"Colour of the week day letters."
),
)
cal_number_colour = ColourSetting(
card=StatsCard,
property_name='cal_number_colour',
display_name=_p(
'skinsettings|page:stats|set:cal_number_colour|display_name',
'Numbers'
),
description=_p(
'skinsettings|page:stats|set:cal_number_colour|desc',
"General calender day colour."
),
)
cal_number_end_colour = ColourSetting(
card=StatsCard,
property_name='cal_number_end_colour',
display_name=_p(
'skinsettings|page:stats|set:cal_number_end_colour|display_name',
'Streak Ends'
),
description=_p(
'skinsettings|page:stats|set:cal_number_end_colour|desc',
"Day colour where streaks start or end."
),
)
cal_streak_middle_colour = ColourSetting(
card=StatsCard,
property_name='cal_streak_middle_colour',
display_name=_p(
'skinsettings|page:stats|set:cal_streak_middle_colour|display_name',
'Streak BG'
),
description=_p(
'skinsettings|page:stats|set:cal_streak_middle_colour|desc',
"Background colour on streak days."
),
)
cal_streak_end_colour = ColourSetting(
card=StatsCard,
property_name='cal_streak_end_colour',
display_name=_p(
'skinsettings|page:stats|set:cal_streak_end_colour|display_name',
'Streak End BG'
),
description=_p(
'skinsettings|page:stats|set:cal_streak_end_colour|desc',
"Background colour where streaks start/end."
),
)
calender_colour_group = SettingGroup(
_p('skinsettings|page:stats|grp:calender_colour', "Calender Colours"),
description=_p(
'skinsettings|page:stats|grp:calender_colour|desc',
"Number and streak colours for the current calender."
),
custom_id='stats-cal',
settings=(
cal_weekday_colour,
cal_number_colour,
cal_number_end_colour,
cal_streak_middle_colour,
cal_streak_end_colour
)
)
base_skin = SkinSetting(
card=StatsCard,
property_name='base_skin_id',
display_name=_p(
'skinsettings|page:stats|set:base_skin|display_name',
'Skin'
),
description=_p(
'skinsettings|page:stats|set:base_skin|desc',
"Select a Skin Preset."
)
)
base_skin_group = SettingGroup(
_p('skinsettings|page:stats|grp:base_skin', "Statistics Skin"),
description=_p(
'skinsettings|page:stats|grp:base_skin|desc',
"Asset pack and default values for this card."
),
custom_id='stats-skin',
settings=(base_skin,),
ungrouped=True
)
stats_page.groups = [base_skin_group, text_colour_group, calender_colour_group]

View File

@@ -0,0 +1,89 @@
from gui.cards import ProfileCard
from ... import babel
from ..skinsetting import ColourSetting, SkinSetting, ColoursSetting
from ..layout import Page, SettingGroup
from . import stats, profile
_p = babel._p
summary_page = Page(
display_name=_p('skinsettings|page:summary|display_name', "Setting Summary"),
editing_description=_p(
'skinsettings|page:summary|edit_desc',
"Simple setup for creating a unified interface theme."
),
preview_description=_p(
'skinsettings|page:summary|preview_desc',
"Summary of common settings across the entire interface."
),
visible_in_preview=True,
render_card=ProfileCard
)
name_colours = ColoursSetting(
profile.header_colour_1,
display_name=_p(
'skinsettings|page:summary|set:name_colours|display_name',
"username colour"
),
description=_p(
'skinsettings|page:summary|set:name_colours|desc',
"Author username colour."
)
)
discrim_colours = ColoursSetting(
profile.header_colour_2,
display_name=_p(
'skinsettings|page:summary|set:discrim_colours|display_name',
"discrim colour"
),
description=_p(
'skinsettings|page:summary|set:discrim_colours|desc',
"Author discriminator colour."
)
)
subheader_colour = ColoursSetting(
stats.header_colour,
profile.subheader_colour,
display_name=_p(
'skinsettings|page:summary|set:subheader_colour|display_name',
"subheadings"
),
description=_p(
'skinsettings|page:summary|set:subheader_colour|desc',
"Colour of subheadings and column headings."
)
)
header_colour_group = SettingGroup(
_p('skinsettings|page:summary|grp:header_colour', "Title Colours"),
description=_p(
'skinsettings|page:summary|grp:header_colour|desc',
"Title and header text colours."
),
custom_id='shared-titles',
settings=(
subheader_colour,
)
)
profile_colour_group = SettingGroup(
_p('skinsettings|page:summary|grp:profile_colour', "Profile Colours"),
description=_p(
'skinsettings|page:summary|grp:profile_colour|desc',
"Profile elements shared across various cards."
),
custom_id='shared-profile',
settings=(
name_colours,
discrim_colours
)
)
summary_page.groups = [header_colour_group, profile_colour_group]

View File

@@ -0,0 +1,350 @@
from gui.cards import WeeklyStatsCard
from ... import babel
from ..skinsetting import ColourSetting, SkinSetting, ColoursSetting
from ..layout import Page, SettingGroup
_p = babel._p
weekly_page = Page(
display_name=_p('skinsettings|page:weekly|display_name', "Weekly Statistics"),
editing_description=_p(
'skinsettings|page:weekly|edit_desc',
"Options for the weekly statistis card."
),
preview_description=None,
visible_in_preview=True,
render_card=WeeklyStatsCard
)
title_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='title_colour',
display_name=_p(
'skinsettings|page:weekly|set:title_colour|display_name',
'Title'
),
description=_p(
'skinsettings|page:weekly|set:title_colour|desc',
"Colour of the card title."
)
)
top_hours_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='top_hours_colour',
display_name=_p(
'skinsettings|page:weekly|set:top_hours_colour|display_name',
'Hours'
),
description=_p(
'skinsettings|page:weekly|set:top_hours_colour|desc',
"Hours axis labels."
)
)
top_hours_bg_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='top_hours_bg_colour',
display_name=_p(
'skinsettings|page:weekly|set:top_hours_bg_colour|display_name',
'Hour Bg'
),
description=_p(
'skinsettings|page:weekly|set:top_hours_bg_colour|desc',
"Hours axis label background."
)
)
top_line_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='top_line_colour',
display_name=_p(
'skinsettings|page:weekly|set:top_line_colour|display_name',
'Lines'
),
description=_p(
'skinsettings|page:weekly|set:top_line_colour|desc',
"Horizontal graph lines."
)
)
top_weekday_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='top_weekday_colour',
display_name=_p(
'skinsettings|page:weekly|set:top_weekday_colour|display_name',
'Weekdays'
),
description=_p(
'skinsettings|page:weekly|set:top_weekday_colour|desc',
"Weekday axis labels."
)
)
top_date_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='top_date_colour',
display_name=_p(
'skinsettings|page:weekly|set:top_date_colour|display_name',
'Dates'
),
description=_p(
'skinsettings|page:weekly|set:top_date_colour|desc',
"Weekday date axis labels."
)
)
top_this_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='top_this_colour',
display_name=_p(
'skinsettings|page:weekly|set:top_this_colour|display_name',
'This Week'
),
description=_p(
'skinsettings|page:weekly|set:top_this_colour|desc',
"This week bar fill colour."
)
)
top_last_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='top_last_colour',
display_name=_p(
'skinsettings|page:weekly|set:top_last_colour|display_name',
'Last Week'
),
description=_p(
'skinsettings|page:weekly|set:top_last_colour|desc',
"Last week bar fill colour."
)
)
btm_weekday_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='btm_weekday_colour',
display_name=_p(
'skinsettings|page:weekly|set:btm_weekday_colour|display_name',
'Weekdays'
),
description=_p(
'skinsettings|page:weekly|set:btm_weekday_colour|desc',
"Weekday axis labels."
)
)
btm_weekly_background_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='btm_weekly_background_colour',
display_name=_p(
'skinsettings|page:weekly|set:btm_weekly_background_colour|display_name',
'Weekday Bg'
),
description=_p(
'skinsettings|page:weekly|set:btm_weekly_background_colour|desc',
"Weekday axis background."
)
)
btm_bar_horiz_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='btm_bar_horiz_colour',
display_name=_p(
'skinsettings|page:weekly|set:btm_bar_horiz_colour|display_name',
'Bars (Horiz)'
),
description=_p(
'skinsettings|page:weekly|set:btm_bar_horiz_colour|desc',
"Horizontal graph bars."
)
)
btm_bar_vert_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='btm_bar_vert_colour',
display_name=_p(
'skinsettings|page:weekly|set:btm_bar_vert_colour|display_name',
'Bars (Vertical)'
),
description=_p(
'skinsettings|page:weekly|set:btm_bar_vert_colour|desc',
"Vertical graph bars."
)
)
btm_this_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='btm_this_colour',
display_name=_p(
'skinsettings|page:weekly|set:btm_this_colour|display_name',
'This Week'
),
description=_p(
'skinsettings|page:weekly|set:btm_this_colour|desc',
"This week bar fill colour."
)
)
btm_last_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='btm_last_colour',
display_name=_p(
'skinsettings|page:weekly|set:btm_last_colour|display_name',
'Last Week'
),
description=_p(
'skinsettings|page:weekly|set:btm_last_colour|desc',
"Last week bar fill colour."
)
)
btm_day_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='btm_day_colour',
display_name=_p(
'skinsettings|page:weekly|set:btm_day_colour|display_name',
'Hours'
),
description=_p(
'skinsettings|page:weekly|set:btm_day_colour|desc',
"Hour axis labels."
)
)
this_week_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='this_week_colour',
display_name=_p(
'skinsettings|page:weekly|set:this_week_colour|display_name',
'This Week Legend'
),
description=_p(
'skinsettings|page:weekly|set:this_week_colour|desc',
"This week legend text."
)
)
last_week_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='last_week_colour',
display_name=_p(
'skinsettings|page:weekly|set:last_week_colour|display_name',
'Last Week Legend'
),
description=_p(
'skinsettings|page:weekly|set:last_week_colour|desc',
"Last week legend text."
)
)
legend_colour = ColoursSetting(
this_week_colour,
last_week_colour,
display_name=_p(
'skinsettings|page:weekly|set:legend_colour|display_name',
'Legend'
),
description=_p(
'skinsettings|page:weekly|set:legend_colour|desc',
"Legend text colour."
)
)
footer_colour = ColourSetting(
card=WeeklyStatsCard,
property_name='footer_colour',
display_name=_p(
'skinsettings|page:weekly|set:footer_colour|display_name',
'Footer'
),
description=_p(
'skinsettings|page:weekly|set:footer_colour|desc',
"Footer text colour."
)
)
base_skin = SkinSetting(
card=WeeklyStatsCard,
property_name='base_skin_id',
display_name=_p(
'skinsettings|page:weekly|set:base_skin|display_name',
'Skin'
),
description=_p(
'skinsettings|page:weekly|set:base_skin|desc',
"Select a Skin Preset."
)
)
base_skin_group = SettingGroup(
_p('skinsettings|page:weekly|grp:base_skin', "Weekly Stats Skin"),
description=_p(
'skinsettings|page:weekly|grp:base_skin|desc',
"Asset pack and default values for this card."
),
custom_id='weekly-skin',
settings=(base_skin,),
ungrouped=True
)
top_colour_group = SettingGroup(
_p('skinsettings|page:weekly|grp:top_colour', "Top Graph"),
description=_p(
'skinsettings|page:weekly|grp:top_colour|desc',
"Customise the top graph colourscheme."
),
custom_id='weekly-top',
settings=(
top_hours_colour,
top_weekday_colour,
top_date_colour,
top_this_colour,
top_last_colour,
)
)
bottom_colour_group = SettingGroup(
_p('skinsettings|page:weekly|grp:bottom_colour', "Bottom Graph"),
description=_p(
'skinsettings|page:weekly|grp:bottom_colour|desc',
"Customise the bottom graph colourscheme."
),
custom_id='weekly-bottom',
settings=(
btm_weekday_colour,
btm_day_colour,
btm_this_colour,
btm_last_colour,
btm_bar_horiz_colour,
)
)
misc_group = SettingGroup(
_p('skinsettings|page:weekly|grp:misc', "Misc Colours"),
description=_p(
'skinsettings|page:weekly|grp:misc|desc',
"Miscellaneous card colours."
),
custom_id='weekly-misc',
settings=(
title_colour,
legend_colour,
footer_colour,
)
)
base_skin = SkinSetting(
card=WeeklyStatsCard,
property_name='base_skin_id',
display_name=_p(
'skinsettings|page:weekly|set:base_skin|display_name',
'Skin'
),
description=_p(
'skinsettings|page:weekly|set:base_skin|desc',
"Select a Skin Preset."
)
)
base_skin_group = SettingGroup(
_p('skinsettings|page:weekly|grp:base_skin', "Weekly Stats Skin"),
description=_p(
'skinsettings|page:weekly|grp:base_skin|desc',
"Asset pack and default values for the Weekly Statistics."
),
custom_id='weekly-skin',
settings=(base_skin,),
ungrouped=True
)
weekly_page.groups = [
base_skin_group,
top_colour_group,
bottom_colour_group,
misc_group,
]

View File

@@ -0,0 +1,438 @@
from gui.cards import WeeklyGoalCard
from ... import babel
from ..skinsetting import ColourSetting, SkinSetting, ColoursSetting
from ..layout import Page, SettingGroup
_p = babel._p
"""
mini_profile_name_colour
mini_profile_badge_colour
mini_profile_badge_text_colour
mini_profile_discrim_colour
title_colour
footer_colour
progress_bg_colour
progress_colour
[attendance_rate_colour, task_count_colour, studied_hour_colour]
[attendance_colour, task_done_colour, studied_text_colour, task_goal_colour]
task_goal_number_colour
task_header_colour
task_done_number_colour
task_undone_number_colour
task_done_text_colour
task_undone_text_colour
"""
weekly_goal_page = Page(
display_name=_p('skinsettings|page:weekly_goal|display_name', "Weekly Goals"),
editing_description=_p(
'skinsettings|page:weekly_goal|edit_desc',
"Options for the weekly goal card."
),
preview_description=None,
visible_in_preview=True,
render_card=WeeklyGoalCard
)
title_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='title_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:title_colour|display_name',
"Title"
),
description=_p(
'skinsettings|page:weekly_goal|set:title_colour|desc',
""
)
)
progress_bg_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='progress_bg_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:progress_bg_colour|display_name',
"Bar Bg"
),
description=_p(
'skinsettings|page:weekly_goal|set:progress_bg_colour|desc',
""
)
)
progress_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='progress_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:progress_colour|display_name',
"Bar Fg"
),
description=_p(
'skinsettings|page:weekly_goal|set:progress_colour|desc',
""
)
)
attendance_rate_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='attendance_rate_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:attendance_rate_colour|display_name',
""
),
description=_p(
'skinsettings|page:weekly_goal|set:attendance_rate_colour|desc',
""
)
)
attendance_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='attendance_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:attendance_colour|display_name',
""
),
description=_p(
'skinsettings|page:weekly_goal|set:attendance_colour|desc',
""
)
)
task_count_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='task_count_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:task_count_colour|display_name',
""
),
description=_p(
'skinsettings|page:weekly_goal|set:task_count_colour|desc',
""
)
)
task_done_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='task_done_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:task_done_colour|display_name',
""
),
description=_p(
'skinsettings|page:weekly_goal|set:task_done_colour|desc',
""
)
)
task_goal_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='task_goal_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:task_goal_colour|display_name',
""
),
description=_p(
'skinsettings|page:weekly_goal|set:task_goal_colour|desc',
""
)
)
task_goal_number_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='task_goal_number_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:task_goal_number_colour|display_name',
"Task Goal"
),
description=_p(
'skinsettings|page:weekly_goal|set:task_goal_number_colour|desc',
""
)
)
studied_text_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='studied_text_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:studied_text_colour|display_name',
""
),
description=_p(
'skinsettings|page:weekly_goal|set:studied_text_colour|desc',
""
)
)
studied_hour_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='studied_hour_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:studied_hour_colour|display_name',
""
),
description=_p(
'skinsettings|page:weekly_goal|set:studied_hour_colour|desc',
""
)
)
text_highlight_colour = ColoursSetting(
attendance_rate_colour,
task_count_colour,
studied_hour_colour,
display_name=_p(
'skinsettings|page:weekly_goal|set:text_highlight_colour|display_name',
"Highlight Text"
),
description=_p(
'skinsettings|page:weekly_goal|set:text_highlight_colour|desc',
"Progress text colour."
)
)
text_colour = ColoursSetting(
attendance_colour,
task_done_colour,
studied_text_colour,
task_goal_colour,
display_name=_p(
'skinsettings|page:weekly_goal|set:text_colour|display_name',
"Text"
),
description=_p(
'skinsettings|page:weekly_goal|set:text_colour|desc',
"Achievement description text colour."
)
)
task_header_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='task_header_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:task_header_colour|display_name',
"Task Header"
),
description=_p(
'skinsettings|page:weekly_goal|set:task_header_colour|desc',
""
)
)
task_done_number_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='task_done_number_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:task_done_number_colour|display_name',
"Checked Number"
),
description=_p(
'skinsettings|page:weekly_goal|set:task_done_number_colour|desc',
""
)
)
task_undone_number_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='task_undone_number_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:task_undone_number_colour|display_name',
"Unchecked Number"
),
description=_p(
'skinsettings|page:weekly_goal|set:task_undone_number_colour|desc',
""
)
)
task_done_text_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='task_done_text_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:task_done_text_colour|display_name',
"Checked Text"
),
description=_p(
'skinsettings|page:weekly_goal|set:task_done_text_colour|desc',
""
)
)
task_undone_text_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='task_undone_text_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:task_undone_text_colour|display_name',
"Unchecked Text"
),
description=_p(
'skinsettings|page:weekly_goal|set:task_undone_text_colour|desc',
""
)
)
footer_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='footer_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:footer_colour|display_name',
"Footer"
),
description=_p(
'skinsettings|page:weekly_goal|set:footer_colour|desc',
""
)
)
mini_profile_badge_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='mini_profile_badge_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:mini_profile_badge_colour|display_name',
'Badge Background'
),
description=_p(
'skinsettings|page:weekly_goal|set:mini_profile_badge_colour|desc',
"Mini-profile badge background colour."
)
)
mini_profile_badge_text_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='mini_profile_badge_text_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:mini_profile_badge_text_colour|display_name',
'Badge Text'
),
description=_p(
'skinsettings|page:weekly_goal|set:mini_profile_badge_text_colour|desc',
""
)
)
mini_profile_name_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='mini_profile_name_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:mini_profile_name_colour|display_name',
'Username'
),
description=_p(
'skinsettings|page:weekly_goal|set:mini_profile_name_colour|desc',
"Mini-profile username colour."
)
)
mini_profile_discrim_colour = ColourSetting(
card=WeeklyGoalCard,
property_name='mini_profile_discrim_colour',
display_name=_p(
'skinsettings|page:weekly_goal|set:mini_profile_discrim_colour|display_name',
'Discriminator'
),
description=_p(
'skinsettings|page:weekly_goal|set:mini_profile_discrim_colour|desc',
"Mini-profile discriminator colour."
)
)
mini_profile_group = SettingGroup(
_p('skinsettings|page:weekly_goal|grp:mini_profile', "Profile"),
description=_p(
'skinsettings|page:weekly_goal|grp:mini_profile|desc',
"Customise the mini-profile shown above the goals."
),
custom_id='weeklygoal-profile',
settings=(
mini_profile_name_colour,
mini_profile_discrim_colour,
mini_profile_badge_colour,
mini_profile_badge_text_colour,
)
)
misc_group = SettingGroup(
_p('skinsettings|page:weekly_goal|grp:misc', "Miscellaneous"),
description=_p(
'skinsettings|page:weekly_goal|grp:misc|desc',
"Other miscellaneous colours."
),
custom_id='weeklygoal-misc',
settings=(
title_colour,
footer_colour,
)
)
task_colour_group = SettingGroup(
_p('skinsettings|page:weekly_goal|grp:task_colour', "Task colours"),
description=_p(
'skinsettings|page:weekly_goal|grp:task_colour|desc',
"Text and number colours for (in)complete goals."
),
custom_id='weeklygoal-tasks',
settings=(
task_undone_number_colour,
task_done_number_colour,
task_undone_text_colour,
task_done_text_colour,
)
)
progress_colour_group = SettingGroup(
_p('skinsettings|page:weekly_goal|grp:progress_colour', "Progress Colours"),
description=_p(
'skinsettings|page:weekly_goal|grp:progress_colour|desc',
"Customise colours for the weekly achievement progress."
),
custom_id='weeklygoal-progress',
settings=(
progress_bg_colour,
progress_colour,
text_colour,
text_highlight_colour,
task_goal_number_colour
)
)
base_skin = SkinSetting(
card=WeeklyGoalCard,
property_name='base_skin_id',
display_name=_p(
'skinsettings|page:weekly_goal|set:base_skin|display_name',
'Skin'
),
description=_p(
'skinsettings|page:weekly_goal|set:base_skin|desc',
"Select a Skin Preset."
)
)
base_skin_group = SettingGroup(
_p('skinsettings|page:weekly_goal|grp:base_skin', "Weekly Goals Skin"),
description=_p(
'skinsettings|page:weekly_goal|grp:base_skin|desc',
"Asset pack and default values for the Weekly Goals."
),
custom_id='weeklygoals-skin',
settings=(base_skin,),
ungrouped=True
)
weekly_goal_page.groups = [
base_skin_group,
mini_profile_group,
misc_group,
progress_colour_group,
task_colour_group
]

View File

@@ -0,0 +1,598 @@
from io import StringIO
import json
from typing import Optional
import asyncio
import datetime as dt
import discord
from discord.ui.button import button, Button, ButtonStyle
from discord.ui.select import select, Select, SelectOption
from gui.base.AppSkin import AppSkin
from meta import LionBot, conf
from meta.errors import ResponseTimedOut, UserInputError
from meta.logger import log_wrap
from utils.ui import FastModal, Confirm, MessageUI, error_handler_for, ModalRetryUI, AButton, AsComponents
from utils.lib import MessageArgs, utc_now
from constants import DATA_VERSION
from .. import babel, logger
from ..skinlib import CustomSkin, FrozenCustomSkin, appskin_as_option
from .pages import pages
from .skinsetting import Setting, SettingInputType, SkinSetting
from .layout import SettingGroup, Page
_p = babel._p
class SettingInput(FastModal):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
@error_handler_for(UserInputError)
async def rerequest(self, interaction, error):
await ModalRetryUI(self, error.msg).respond_to(interaction)
class CustomSkinEditor(MessageUI):
def _init_children(self):
# HACK to stop ViewWeights complaining that this UI has too many children
# Children will be correctly initialised after parent init.
return []
def __init__(self, skin: CustomSkin, **kwargs):
super().__init__(timeout=600, **kwargs)
self._children = super()._init_children()
self.skin = skin
self.bot = skin.bot
self.cog = self.bot.get_cog('CustomSkinCog')
self.global_themes = self._get_available()
# UI State
# Whether we are currently in customisation mode
self.customising = False
self.page_index = 0
self.showing_skin_setting: Optional[SkinSetting] = None
# Last item in history is current state
# Last item in future is next state
self.history = [skin.freeze()]
self.future = []
self.dirty = False
@property
def page(self) -> Page:
return pages[self.page_index]
# ----- UI API -----
def push_state(self):
"""
Push a state onto the history stack.
Run this on each change _before_ the refresh.
"""
state = self.skin.freeze()
self.history.append(state)
self.future.clear()
self.dirty = True
def _get_available(self) -> dict[str, AppSkin]:
skins = {
skin.skin_id: skin for skin in AppSkin.get_all()
if skin.public
}
skins['default'] = self._make_default()
return skins
def _make_default(self) -> AppSkin:
"""
Create a placeholder 'default' skin.
"""
t = self.bot.translator.t
skin = AppSkin(None)
skin.skin_id = 'default'
skin.display_name = t(_p(
'ui:skineditor|default_skin:display_name',
"Default"
))
skin.description = t(_p(
'ui:skineditor|default_skin:description',
"My default interface theme"
))
skin.price = 0
return skin
# ----- UI Components -----
# Download button
# NOTE: property_id, card_id, property_name, value
# Metadata with version, time generated, skinid, generating user
# Special field for the global_skin_id
@button(
label="DOWNLOAD_BUTTON_PLACEHOLDER",
style=ButtonStyle.blurple
)
async def download_button(self, press: discord.Interaction, pressed: Button):
await press.response.defer(thinking=True, ephemeral=True)
data = {}
data['metadata'] = {
'requested_by': press.user.id,
'requested_in': press.guild.id if press.guild else None,
'skinid': self.skin.skinid,
'created_at': utc_now().isoformat(),
'data_version': DATA_VERSION,
}
data['custom_skin'] = {
'skinid': self.skin.skinid,
'base_skin': self.skin.base_skin_name,
}
properties = {}
for card, card_props in self.skin.properties.items():
props = {}
for name, value in card_props.items():
propid = (await self.cog.fetch_property_ids((card, name)))[0]
props[name] = {
'property_id': propid,
'value': value
}
properties[card] = props
data['custom_skin']['properties'] = properties
content = json.dumps(data, indent=2)
with StringIO(content) as fp:
fp.seek(0)
file = discord.File(fp, filename=f"skin-{self.skin.skinid}.json")
await press.followup.send("Here is your custom skin data!", file=file, ephemeral=True)
async def download_button_refresh(self):
button = self.download_button
t = self.bot.translator.t
button.label = t(_p(
'ui:skineditor|button:download|label',
"Download"
))
# Save button
@button(
label="SAVE_BUTTON_PLACEHOLDER",
style=ButtonStyle.green
)
async def save_button(self, press: discord.Interaction, pressed: Button):
await press.response.defer(thinking=True, ephemeral=True)
await self.skin.save()
self.history = self.history[-1:]
self.dirty = False
await self.refresh(thinking=press)
async def save_button_refresh(self):
button = self.save_button
t = self.bot.translator.t
button.label = t(_p(
'ui:skineditor|button:save|label',
"Save"
))
button.disabled = not self.dirty
# Back button
@button(
label="BACK_BUTTON_PLACEHOLDER",
style=ButtonStyle.red
)
async def back_button(self, press: discord.Interaction, pressed: Button):
await press.response.defer(thinking=True, ephemeral=True)
self.customising = False
await self.refresh(thinking=press)
async def back_button_refresh(self):
button = self.back_button
t = self.bot.translator.t
button.label = t(_p(
'ui:skineditor|button:back|label',
"Back"
))
# Customise button
@button(
label="CUSTOMISE_BUTTON_PLACEHOLDER",
style=ButtonStyle.green
)
async def customise_button(self, press: discord.Interaction, pressed: Button):
await press.response.defer(thinking=True, ephemeral=True)
self.customising = True
await self.refresh(thinking=press)
async def customise_button_refresh(self):
button = self.customise_button
t = self.bot.translator.t
button.label = t(_p(
'ui:skineditor|button:customise|label',
"Customise"
))
# Reset card button
@button(
label="RESET_CARD_BUTTON_PLACEHOLDER",
style=ButtonStyle.red
)
async def reset_card_button(self, press: discord.Interaction, pressed: Button):
# Note this actually resets the page, not the card
await press.response.defer(thinking=True, ephemeral=True)
for group in self.page.groups:
for setting in group.settings:
setting.set_in(self.skin, None)
self.push_state()
await self.refresh(thinking=press)
async def reset_card_button_refresh(self):
button = self.reset_card_button
t = self.bot.translator.t
button.label = t(_p(
'ui:skineditor|button:reset_card|label',
"Reset Card"
))
# Reset all button
@button(
label="RESET_ALL_BUTTON_PLACEHOLDER",
style=ButtonStyle.red
)
async def reset_all_button(self, press: discord.Interaction, pressed: Button):
await press.response.defer(thinking=True, ephemeral=True)
self.skin.properties.clear()
self.skin.base_skin_name = None
self.push_state()
await self.refresh(thinking=press)
async def reset_all_button_refresh(self):
button = self.reset_all_button
t = self.bot.translator.t
button.label = t(_p(
'ui:skineditor|button:reset_all|label',
"Reset All"
))
# Page selector
@select(
cls=Select,
placeholder="PAGE_MENU_PLACEHOLDER",
min_values=1, max_values=1
)
async def page_menu(self, selection: discord.Interaction, selected: Select):
await selection.response.defer(thinking=True, ephemeral=True)
self.page_index = int(selected.values[0])
self.showing_skin_setting = None
await self.refresh(thinking=selection)
async def page_menu_refresh(self):
menu = self.page_menu
t = self.bot.translator.t
options = []
if not self.customising:
menu.placeholder = t(_p(
'ui:skineditor|menu:page|placeholder:preview',
"Select a card to preview"
))
for i, page in enumerate(pages):
if page.visible_in_preview:
option = SelectOption(
label=t(page.display_name),
value=str(i),
description=t(page.preview_description) if page.preview_description else None
)
option.default = (i == self.page_index)
options.append(option)
else:
menu.placeholder = t(_p(
'ui:skineditor|menu:page|placeholder:edit',
"Select a card to customise"
))
for i, page in enumerate(pages):
option = SelectOption(
label=t(page.display_name),
value=str(i),
description=t(page.editing_description) if page.editing_description else None
)
option.default = (i == self.page_index)
options.append(option)
menu.options = options
# Setting group selector
@select(
cls=Select,
placeholder="GROUP_MENU_PLACEHOLDER",
min_values=1, max_values=1
)
async def group_menu(self, selection: discord.Interaction, selected: Select):
groupid = selected.values[0]
group = next(group for group in self.page.groups if group.custom_id == groupid)
if group.settings[0].input_type is SettingInputType.SkinInput:
self.showing_skin_setting = group.settings[0]
await selection.response.defer(thinking=True, ephemeral=True)
await self.refresh(thinking=selection)
else:
await self._launch_group_editor(selection, group)
async def _launch_group_editor(self, interaction: discord.Interaction, group: SettingGroup):
t = self.bot.translator.t
editable = group.editable_settings
items = [
setting.make_input_field(self.skin)
for setting in editable
]
modal = SettingInput(*items, title=t(group.name))
@modal.submit_callback()
async def group_modal_callback(interaction: discord.Interaction):
values = []
for item, setting in zip(items, editable):
value = await setting.parse_input(self.skin, item.value)
values.append(value)
await interaction.response.defer(thinking=True, ephemeral=True)
for value, setting in zip(values, editable):
setting.set_in(self.skin, value)
self.push_state()
await self.refresh(thinking=interaction)
await interaction.response.send_modal(modal)
async def group_menu_refresh(self):
menu = self.group_menu
t = self.bot.translator.t
menu.placeholder = t(_p(
'ui:skineditor|menu:group|placeholder',
"Select a group or option to customise"
))
options = []
for group in self.page.groups:
option = group.select_option_for(self.skin)
options.append(option)
menu.options = options
# Base skin selector
@select(
cls=Select,
placeholder="SKIN_MENU_PLACEHOLDER",
min_values=1, max_values=1
)
async def skin_menu(self, selection: discord.Interaction, selected: Select):
await selection.response.defer(thinking=True, ephemeral=True)
skin_id = selected.values[0]
if skin_id == 'default':
skin_id = None
if self.customising:
if self.showing_skin_setting:
# Update the current page card with this skin id.
self.showing_skin_setting.set_in(self.skin, skin_id)
else:
# Far more brutal
# Update the global base skin id, and wipe the base skin id for each card
self.skin.base_skin_name = skin_id
for card_id in self.skin.properties:
self.skin.set_prop(card_id, 'base_skin_id', None)
self.push_state()
await self.refresh(thinking=selection)
async def skin_menu_refresh(self):
menu = self.skin_menu
t = self.bot.translator.t
menu.placeholder = t(_p(
'ui:skineditor|menu:skin|placeholder',
"Select a theme"
))
options = []
for skin in self.global_themes.values():
option = appskin_as_option(skin)
options.append(option)
menu.options = options
# Quit button, with confirmation
@button(style=ButtonStyle.grey, emoji=conf.emojis.cancel)
async def quit_button(self, press: discord.Interaction, pressed: Button):
# Confirm quit if there are unsaved changes
if self.dirty:
t = self.bot.translator.t
confirm_msg = t(_p(
'ui:skineditor|button:quit|confirm',
"You have unsaved changes! Are you sure you want to quit?"
))
confirm = Confirm(confirm_msg, self._callerid)
confirm.confirm_button.label = t(_p(
'ui:skineditor|button:quit|confirm|button:yes',
"Yes, Quit Now"
))
confirm.confirm_button.style = ButtonStyle.red
confirm.cancel_button.style = ButtonStyle.green
confirm.cancel_button.label = t(_p(
'ui:skineditor|button:quit|confirm|button:no',
"No, Go Back"
))
try:
result = await confirm.ask(press, ephemeral=True)
except ResponseTimedOut:
result = False
if result:
await self.quit()
else:
await self.quit()
@button(label="UNDO_BUTTON_PLACEHOLDER", style=ButtonStyle.blurple)
async def undo_button(self, press: discord.Interaction, pressed: Button):
"""
Pop the history stack.
"""
if len(self.history) > 1:
state = self.history.pop()
self.future.append(state)
current = self.history[-1]
self.skin.load_frozen(current)
await press.response.defer(thinking=True, ephemeral=True)
await self.refresh(thinking=press)
async def undo_button_refresh(self):
t = self.bot.translator.t
button = self.undo_button
button.label = t(_p(
'ui:skineditor|button:undo|label',
"Undo"
))
button.disabled = (len(self.history) <= 1)
@button(label="REDO_BUTTON_PLACEHOLDER", style=ButtonStyle.blurple)
async def redo_button(self, press: discord.Interaction, pressed: Button):
"""
Pop the future stack.
"""
if len(self.future) > 0:
state = self.future.pop()
self.history.append(state)
current = self.history[-1]
self.skin.load_frozen(current)
await press.response.defer(thinking=True, ephemeral=True)
await self.refresh(thinking=press)
async def redo_button_refresh(self):
t = self.bot.translator.t
button = self.redo_button
button.label = t(_p(
'ui:skineditor|button:redo|label',
"Redo"
))
button.disabled = (len(self.future) == 0)
# ----- UI Flow -----
async def make_message(self) -> MessageArgs:
page = self.page
embed = page.make_embed_for(self.skin)
if page.render_card is not None:
args = self.skin.args_for(page.render_card.card_id)
args.setdefault('base_skin_id', self.cog.current_default)
file = await page.render_card.generate_sample(skin=args)
files = [file]
else:
files = []
return MessageArgs(embed=embed, files=files)
async def refresh_layout(self):
"""
Customising mode:
(card_menu)
(skin_menu?)
(download, save, undo, redo, back,)
Other:
(card_menu)
(theme_menu)
(customise, save, reset_card, reset_all, X)
"""
to_refresh = (
self.page_menu_refresh(),
self.skin_menu_refresh(),
self.undo_button_refresh(),
self.redo_button_refresh(),
self.reset_card_button_refresh(),
self.reset_all_button_refresh(),
self.customise_button_refresh(),
self.back_button_refresh(),
self.save_button_refresh(),
self.group_menu_refresh(),
self.download_button_refresh(),
)
await asyncio.gather(*to_refresh)
if self.customising:
self.set_layout(
(self.page_menu,),
(self.group_menu,),
(self.skin_menu,) if self.showing_skin_setting else (),
(
self.save_button,
self.undo_button,
self.redo_button,
self.download_button,
self.back_button,
),
)
else:
self.set_layout(
(self.page_menu,),
(self.skin_menu,),
(
self.customise_button,
self.save_button,
self.reset_card_button,
self.reset_all_button,
self.quit_button,
),
)
async def reload(self):
...
async def pre_timeout(self):
# Timeout confirmation
if self.dirty:
t = self.bot.translator.t
grace_period = 60
grace_time = utc_now() + dt.timedelta(seconds=grace_period)
embed = discord.Embed(
title=t(_p(
'ui:skineditor|timeout_warning|title',
"Warning!"
)),
description=t(_p(
'ui:skineditor|timeout_warning|desc',
"This interface will time out {timestamp}. Press 'Continue' below to keep editing."
)).format(
timestamp=discord.utils.format_dt(grace_time, style='R')
),
)
components = None
stopped = False
@AButton(label=t(_p('ui:skineditor|timeout_warning|continue', "Continue")), style=ButtonStyle.green)
async def cont_button(interaction: discord.Interaction, pressed):
await interaction.response.defer()
if interaction.message:
await interaction.message.delete()
nonlocal stopped
stopped = True
# TODO: Clean up this mess. It works, but needs to be refactored to a timeout confirmation mixin.
# TODO: Consider moving the message to the interaction response
self._refresh_timeout()
components.stop()
components = AsComponents(cont_button, timeout=grace_period)
channel = self._original.channel if self._original else self._message.channel
message = await channel.send(content=f"<@{self._callerid}>", embed=embed, view=components)
await components.wait()
if not stopped:
try:
await message.delete()
except discord.HTTPException:
pass

View File

@@ -0,0 +1,298 @@
from typing import Literal, Optional
from enum import Enum
from discord.ui import TextInput
from meta import LionBot
from meta.errors import UserInputError
from babel.translator import LazyStr
from gui.base import Card, FieldDesc, AppSkin
from .. import babel
from ..skinlib import CustomSkin
_p = babel._p
class SettingInputType(Enum):
SkinInput = -1
ModalInput = 0
MenuInput = 1
ButtonInput = 2
class Setting:
"""
An abstract base interface for a custom skin 'setting'.
A skin setting is considered to be some readable and usually writeable
information extractable from a `CustomSkin`.
This will usually consist of the value of one or more properties,
which are themselves associated to fields of GUI Cards.
The methods in this ABC describe the interface for such a setting.
Each method accepts a `CustomSkin`,
and an implementation should describe how to
get, set, parse, format, or display the setting
for that given skin.
This is very similar to how Settings are implemented in the bot,
except here all settings have a shared external source of state, the CustomSkin.
Thus, each setting is simply an instance of an appropriate setting class,
rather than a class itself.
"""
# What type of input method this setting requires for input
input_type: SettingInputType = SettingInputType.ModalInput
def __init__(self, *args, display_name, description, **kwargs):
self.display_name: LazyStr = display_name
self.description: LazyStr = description
def default_value_in(self, skin: CustomSkin) -> Optional[str]:
"""
The default value of this setting in this skin.
This takes into account base skin data and localisation.
May be `None` if the setting does not have a default value.
"""
raise NotImplementedError
def value_in(self, skin: CustomSkin) -> Optional[str]:
"""
The current value of this setting from this skin.
May be None if the setting is not set or does not have a value.
Usually should not take into account defaults.
"""
raise NotImplementedError
def set_in(self, skin: CustomSkin, value: Optional[str]):
"""
Set this setting to the given value in this skin.
"""
raise NotImplementedError
def format_value_in(self, skin: CustomSkin, value: Optional[str]) -> str:
"""
Format the given setting value for display (typically in a setting table).
"""
raise NotImplementedError
async def parse_input(self, skin: CustomSkin, userstr: str) -> Optional[str]:
"""
Parse a user provided string into a value for this setting.
Will raise 'UserInputError' with a readable message if parsing fails.
"""
raise NotImplementedError
def make_input_field(self, skin: CustomSkin) -> TextInput:
"""
Create a TextInput field for this setting, using the current value.
"""
raise NotImplementedError
class PropertySetting(Setting):
"""
A skin setting corresponding to a single property of a single card.
Note that this is still abstract,
as it does not implement any formatting or parsing methods.
This will usually (but may not always) correspond to a single Field of the card skin.
"""
def __init__(self, card: type[Card], property_name: str, **kwargs):
super().__init__(**kwargs)
self.card = card
self.property_name = property_name
@property
def card_id(self):
"""
The `card_id` of the Card class this setting belongs to.
"""
return self.card.card_id
@property
def field(self) -> Optional[FieldDesc]:
"""
The CardSkin field overwrriten by this setting, if it exists.
"""
return self.card.skin._fields.get(self.property_name, None)
def default_value_in(self, skin: CustomSkin) -> Optional[str]:
"""
For a PropertySetting, the default value is determined as follows:
base skin value from:
- card base skin
- custom base skin
- global app base skin
fallback (field) value from the CardSkin
"""
base_skin = skin.get_prop(self.card_id, 'base_skin_id')
base_skin = base_skin or skin.base_skin_name
base_skin = base_skin or skin.cog.current_default
app_skin_args = AppSkin.get(base_skin).for_card(self.card_id)
if self.property_name in app_skin_args:
return app_skin_args[self.property_name]
elif self.field:
return self.field.default
else:
return None
def value_in(self, skin: CustomSkin) -> Optional[str]:
return skin.get_prop(self.card_id, self.property_name)
def set_in(self, skin: CustomSkin, value: Optional[str]):
skin.set_prop(self.card_id, self.property_name, value)
class _ColourInterface(Setting):
"""
Skin setting mixin for parsing and formatting colour typed settings.
"""
def format_value_in(self, skin: CustomSkin, value: Optional[str]) -> str:
if value:
formatted = f"`{value}`"
else:
formatted = skin.bot.translator.t(_p(
'skinsettings|colours|format:not_set',
"Not Set"
))
return formatted
async def parse_input(self, skin: CustomSkin, userstr: str) -> Optional[str]:
stripped = userstr.strip('# ').upper()
if not stripped:
value = None
elif len(stripped) not in (6, 8) or any(c not in '0123456789ABCDEF' for c in stripped):
raise UserInputError(
skin.bot.translator.t(_p(
'skinsettings|colours|parse|error:invalid',
"Could not parse `{given}` as a colour!"
" Please use RGB/RGBA format (e.g. `#ABABABF0`)."
)).format(given=userstr)
)
else:
value = f"#{stripped}"
return value
def make_input_field(self, skin: CustomSkin) -> TextInput:
t = skin.bot.translator.t
value = self.value_in(skin)
default_value = self.default_value_in(skin)
label = t(self.display_name)
default = value
if default_value:
placeholder = f"{default_value} ({t(self.description)})"
else:
placeholder = t(self.description)
return TextInput(
label=label,
placeholder=placeholder,
default=default,
min_length=0,
max_length=9,
required=False,
)
class ColourSetting(_ColourInterface, PropertySetting):
"""
A Property skin setting representing a single colour field.
"""
pass
class SkinSetting(PropertySetting):
"""
A Property setting representing the base skin of a card.
"""
input_type = SettingInputType.SkinInput
def format_value_in(self, skin: CustomSkin, value: Optional[str]) -> str:
if value:
app_skin = AppSkin.get(value)
formatted = f"`{app_skin.display_name}`"
else:
formatted = skin.bot.translator.t(_p(
'skinsettings|base_skin|format:not_set',
"Default"
))
return formatted
def default_value_in(self, skin: CustomSkin) -> Optional[str]:
return skin.base_skin_name
class CompoundSetting(Setting):
"""
A Setting combining several PropertySettings across (potentially) multiple cards.
"""
NOTSHARED = ''
def __init__(self, *settings: PropertySetting, **kwargs):
super().__init__(**kwargs)
self.settings = settings
def default_value_in(self, skin: CustomSkin) -> Optional[str]:
"""
The default value of a CompoundSetting is the shared default of the component settings.
If the components do not share a default value, returns None.
"""
value = None
for setting in self.settings:
setting_value = setting.default_value_in(skin)
if setting_value is None:
value = None
break
if value is None:
value = setting_value
elif value != setting_value:
value = None
break
return value
def value_in(self, skin: CustomSkin) -> Optional[str]:
"""
The value of a compound setting is the shared value of the components.
"""
value = self.NOTSHARED
for setting in self.settings:
setting_value = setting.value_in(skin) or setting.default_value_in(skin)
if value is self.NOTSHARED:
value = setting_value
elif value != setting_value:
value = self.NOTSHARED
break
return value
def set_in(self, skin: CustomSkin, value: Optional[str]):
"""
Set all of the components individually.
"""
for setting in self.settings:
setting.set_in(skin, value)
class ColoursSetting(_ColourInterface, CompoundSetting):
"""
Compound setting representing multiple colours.
"""
def format_value_in(self, skin: CustomSkin, value: Optional[str]) -> str:
if value is self.NOTSHARED:
return "Mixed"
elif value is None:
return "Not Set"
else:
return f"`{value}`"

View File

@@ -16,6 +16,7 @@ _p = babel._p
class GlobalSkinSettings(SettingGroup): class GlobalSkinSettings(SettingGroup):
class DefaultSkin(ModelData, StringSetting): class DefaultSkin(ModelData, StringSetting):
setting_id = 'default_app_skin' setting_id = 'default_app_skin'
_event = 'botset_skin'
_write_ward = sys_admin_iward _write_ward = sys_admin_iward
_display_name = _p( _display_name = _p(

View File

@@ -110,7 +110,7 @@ class CustomSkin:
async with conn.transaction(): async with conn.transaction():
skinid = self.skinid skinid = self.skinid
await self.data.update(base_skin_id=self.base_skin_id) await self.data.update(base_skin_id=self.base_skin_id)
await self.cog.data.skin_properties.delete_where(skinid=skinid) await self.cog.data.skin_properties.delete_where(custom_skin_id=skinid)
props = { props = {
(card, name): value (card, name): value
@@ -122,31 +122,37 @@ class CustomSkin:
await self.cog.fetch_property_ids(*props.keys()) await self.cog.fetch_property_ids(*props.keys())
# Now bulk insert # Now bulk insert
await self.cog.data.skin_properties.insert_many( if props:
('custom_skin_id', 'property_id', 'value'), await self.cog.data.skin_properties.insert_many(
*( ('custom_skin_id', 'property_id', 'value'),
(skinid, self.cog.skin_properties[propkey], value) *(
for propkey, value in props.items() (skinid, self.cog.skin_properties.inverse[propkey], value)
for propkey, value in props.items()
)
) )
) await self.bot.global_dispatch('skin_updated', skinid)
def get_prop(self, card_id: str, prop_name: str) -> Optional[str]:
return self.properties.get(card_id, {}).get(prop_name, None)
def set_prop(self, card_id: str, prop_name: str, value: Optional[str]):
cardprops = self.properties.get(card_id, None)
if value is None:
if cardprops is not None:
cardprops.pop(prop_name, None)
else:
if cardprops is None:
cardprops = self.properties[card_id] = {}
cardprops[prop_name] = value
def resolve_propid(self, propid: int) -> tuple[str, str]: def resolve_propid(self, propid: int) -> tuple[str, str]:
return self.cog.skin_properties[propid] return self.cog.skin_properties[propid]
def __getitem__(self, propid: int) -> Optional[str]: def __getitem__(self, propid: int) -> Optional[str]:
card, name = self.resolve_propid(propid) return self.get_prop(*self.resolve_propid(propid))
return self.properties.get(card, {}).get(name, None)
def __setitem__(self, propid: int, value: Optional[str]): def __setitem__(self, propid: int, value: Optional[str]):
card, name = self.resolve_propid(propid) return self.set_prop(*self.resolve_propid(propid), value)
cardprops = self.properties.get(card, None)
if value is None:
if cardprops is not None:
cardprops.pop(name, None)
else:
if cardprops is None:
cardprops = self.properties[card] = {}
cardprops[name] = value
def __delitem__(self, propid: int): def __delitem__(self, propid: int):
card, name = self.resolve_propid(propid) card, name = self.resolve_propid(propid)
@@ -163,7 +169,7 @@ class CustomSkin:
Update state from the given frozen state. Update state from the given frozen state.
""" """
self.base_skin_name = frozen.base_skin_name self.base_skin_name = frozen.base_skin_name
self.properties = dict((card, dict(props)) for card, props in frozen.properties) self.properties = dict((card, dict(props)) for card, props in frozen.properties.items())
return self return self
def args_for(self, card_id: str): def args_for(self, card_id: str):