diff --git a/src/meta/LionBot.py b/src/meta/LionBot.py index 9d514c67..4b32df48 100644 --- a/src/meta/LionBot.py +++ b/src/meta/LionBot.py @@ -13,7 +13,7 @@ from aiohttp import ClientSession from data import Database from utils.lib import tabulate from gui.errors import RenderingException -from babel.translator import ctx_locale +from babel.translator import ctx_locale, LeoBabel from .config import Conf 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): 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, - testing_guilds: List[int] = [], translator=None, **kwargs + testing_guilds: List[int] = [], **kwargs ): kwargs.setdefault('tree_cls', LionTree) super().__init__(*args, **kwargs) diff --git a/src/modules/skins/cog.py b/src/modules/skins/cog.py index 95711066..b142def1 100644 --- a/src/modules/skins/cog.py +++ b/src/modules/skins/cog.py @@ -10,6 +10,7 @@ from frozendict import frozendict from meta import LionCog, LionBot, LionContext +from meta.errors import UserInputError from meta.logger import log_wrap from utils.lib import MISSING, utc_now 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 .settingui import GlobalSkinSettingUI from .userskinui import UserSkinUI +from .editor.skineditor import CustomSkinEditor _p = babel._p @@ -46,6 +48,8 @@ class CustomSkinCog(LionCog): # Cache of custom skin id -> frozen custom skin self.custom_skins: LRUCache[int, FrozenCustomSkin] = LRUCache(maxsize=1000) + self.current_default: Optional[str] = None + async def cog_load(self): await self.data.init() @@ -61,6 +65,7 @@ class CustomSkinCog(LionCog): await self._reload_appskins() await self._reload_property_map() + await self.get_default_skin() async def _reload_property_map(self): """ @@ -123,6 +128,7 @@ class CustomSkinCog(LionCog): """ setting = self.bot_settings.DefaultSkin instance = await setting.get(self.bot.appname) + self.current_default = instance.value return instance.value 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) args.update(skin_args) - default = await self.get_default_skin() + default = self.current_default if default: args.setdefault("base_skin_id", default) @@ -224,11 +230,20 @@ class CustomSkinCog(LionCog): Update cached args for given custom skin id. """ 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: skin = custom_skin.freeze() 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 ----- @LionCog.placeholder_group @cmds.hybrid_group("my", with_app_command=False) @@ -268,8 +283,40 @@ class CustomSkinCog(LionCog): return if not ctx.guild: 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 ----- @LionCog.placeholder_group diff --git a/src/modules/skins/editor/layout.py b/src/modules/skins/editor/layout.py index e69de29b..ad0630ad 100644 --- a/src/modules/skins/editor/layout.py +++ b/src/modules/skins/editor/layout.py @@ -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 diff --git a/src/modules/skins/editor/pages/__init__.py b/src/modules/skins/editor/pages/__init__.py index e69de29b..caa2cf21 100644 --- a/src/modules/skins/editor/pages/__init__.py +++ b/src/modules/skins/editor/pages/__init__.py @@ -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, +] diff --git a/src/modules/skins/editor/pages/leaderboard.py b/src/modules/skins/editor/pages/leaderboard.py index e69de29b..8ce00056 100644 --- a/src/modules/skins/editor/pages/leaderboard.py +++ b/src/modules/skins/editor/pages/leaderboard.py @@ -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 +] + diff --git a/src/modules/skins/editor/pages/monthly.py b/src/modules/skins/editor/pages/monthly.py index e69de29b..20693925 100644 --- a/src/modules/skins/editor/pages/monthly.py +++ b/src/modules/skins/editor/pages/monthly.py @@ -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 +] + diff --git a/src/modules/skins/editor/pages/monthly_goals.py b/src/modules/skins/editor/pages/monthly_goals.py index e69de29b..075bce72 100644 --- a/src/modules/skins/editor/pages/monthly_goals.py +++ b/src/modules/skins/editor/pages/monthly_goals.py @@ -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 +] + diff --git a/src/modules/skins/editor/pages/profile.py b/src/modules/skins/editor/pages/profile.py index e69de29b..d8e07bcf 100644 --- a/src/modules/skins/editor/pages/profile.py +++ b/src/modules/skins/editor/pages/profile.py @@ -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, +] + diff --git a/src/modules/skins/editor/pages/stats.py b/src/modules/skins/editor/pages/stats.py index e69de29b..dd9f86c1 100644 --- a/src/modules/skins/editor/pages/stats.py +++ b/src/modules/skins/editor/pages/stats.py @@ -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] + diff --git a/src/modules/skins/editor/pages/summary.py b/src/modules/skins/editor/pages/summary.py index e69de29b..8f31986d 100644 --- a/src/modules/skins/editor/pages/summary.py +++ b/src/modules/skins/editor/pages/summary.py @@ -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] + diff --git a/src/modules/skins/editor/pages/weekly.py b/src/modules/skins/editor/pages/weekly.py index e69de29b..e4eff376 100644 --- a/src/modules/skins/editor/pages/weekly.py +++ b/src/modules/skins/editor/pages/weekly.py @@ -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, +] + diff --git a/src/modules/skins/editor/pages/weekly_goals.py b/src/modules/skins/editor/pages/weekly_goals.py index e69de29b..f81b449d 100644 --- a/src/modules/skins/editor/pages/weekly_goals.py +++ b/src/modules/skins/editor/pages/weekly_goals.py @@ -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 +] + diff --git a/src/modules/skins/editor/skineditor.py b/src/modules/skins/editor/skineditor.py index e69de29b..f05e66d0 100644 --- a/src/modules/skins/editor/skineditor.py +++ b/src/modules/skins/editor/skineditor.py @@ -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 diff --git a/src/modules/skins/editor/skinsetting.py b/src/modules/skins/editor/skinsetting.py index e69de29b..1ddbdfc6 100644 --- a/src/modules/skins/editor/skinsetting.py +++ b/src/modules/skins/editor/skinsetting.py @@ -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}`" diff --git a/src/modules/skins/settings.py b/src/modules/skins/settings.py index 2c248942..a700076c 100644 --- a/src/modules/skins/settings.py +++ b/src/modules/skins/settings.py @@ -16,6 +16,7 @@ _p = babel._p class GlobalSkinSettings(SettingGroup): class DefaultSkin(ModelData, StringSetting): setting_id = 'default_app_skin' + _event = 'botset_skin' _write_ward = sys_admin_iward _display_name = _p( diff --git a/src/modules/skins/skinlib.py b/src/modules/skins/skinlib.py index 4dc172b3..2ce57141 100644 --- a/src/modules/skins/skinlib.py +++ b/src/modules/skins/skinlib.py @@ -110,7 +110,7 @@ class CustomSkin: async with conn.transaction(): skinid = self.skinid 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 = { (card, name): value @@ -122,31 +122,37 @@ class CustomSkin: await self.cog.fetch_property_ids(*props.keys()) # Now bulk insert - 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() + if props: + await self.cog.data.skin_properties.insert_many( + ('custom_skin_id', 'property_id', 'value'), + *( + (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]: return self.cog.skin_properties[propid] def __getitem__(self, propid: int) -> Optional[str]: - card, name = self.resolve_propid(propid) - return self.properties.get(card, {}).get(name, None) + return self.get_prop(*self.resolve_propid(propid)) def __setitem__(self, propid: int, value: Optional[str]): - card, name = self.resolve_propid(propid) - 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 + return self.set_prop(*self.resolve_propid(propid), value) def __delitem__(self, propid: int): card, name = self.resolve_propid(propid) @@ -163,7 +169,7 @@ class CustomSkin: Update state from the given frozen state. """ 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 def args_for(self, card_id: str):