Skip to content

Commit d9bcf65

Browse files
committed
Add support for Station cards to Borderless Showcase template
1 parent 4403af1 commit d9bcf65

3 files changed

Lines changed: 190 additions & 48 deletions

File tree

manifest.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ borderless-showcase.psd:
4141
- class
4242
- battle
4343
- prototype
44+
- station
4445

4546
borderless-showcase-split.psd:
4647
name: "Borderless Showcase"

py/borderless_showcase.py

Lines changed: 188 additions & 48 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@
3030
PlaneswalkerLayout,
3131
PrototypeLayout,
3232
SplitLayout,
33+
StationLayout,
3334
)
3435
from src.schema.adobe import EffectGradientOverlay, EffectStroke, LayerEffects
3536
from src.schema.colors import ColorObject, GradientColor
@@ -38,6 +39,7 @@
3839
from src.templates.planeswalker import PlaneswalkerMod
3940
from src.templates.saga import SagaMod
4041
from src.templates.split import SplitMod
42+
from src.templates.station import StationMod
4143
from src.templates.transform import TransformMod
4244
from src.text_layers import FormattedTextArea, FormattedTextField, TextField
4345
from src.utils.adobe import LayerObjectTypes, ReferenceLayer
@@ -74,7 +76,13 @@ class TextboxSizingArgs(TypedDict):
7476

7577

7678
class BorderlessShowcase(
77-
SplitMod, VerticalMod, PlaneswalkerMod, AdventureMod, LevelerMod, BackupAndRestore
79+
SplitMod,
80+
VerticalMod,
81+
PlaneswalkerMod,
82+
AdventureMod,
83+
LevelerMod,
84+
StationMod,
85+
BackupAndRestore,
7886
):
7987
# region Constants
8088

@@ -292,10 +300,24 @@ def supports_dynamic_textbox_height(self) -> bool:
292300
and not self.is_leveler
293301
)
294302

303+
@cached_property
304+
def is_centered(self) -> bool:
305+
if self.is_station:
306+
return False
307+
return super().is_centered
308+
295309
# endregion Checks
296310

297311
# region Frame Details
298312

313+
@cached_property
314+
def doc_height(self) -> int | float:
315+
return self.docref.height
316+
317+
@cached_property
318+
def doc_width(self) -> int | float:
319+
return self.docref.width
320+
299321
@cached_property
300322
def art_frame_vertical(self) -> str:
301323
if self.bottom_border_type == "Full":
@@ -304,15 +326,12 @@ def art_frame_vertical(self) -> str:
304326

305327
@cached_property
306328
def size(self) -> str:
307-
if self.is_leveler:
308-
return BorderlessTextbox.Tall
309-
310329
if self.supports_dynamic_textbox_height and (
311330
self.textbox_height or self.rules_text_font_size
312331
):
313332
# Return something else than Tall to trigger textbox_positioning
314333
return BorderlessTextbox.Automatic
315-
elif self.is_split:
334+
elif self.is_split or self.is_leveler or self.is_station:
316335
# Disables textbox_positioning
317336
return BorderlessTextbox.Tall
318337

@@ -598,52 +617,142 @@ def textbox_reference(self) -> ReferenceLayer | None:
598617
and self.textbox_reference_base
599618
and self.text_wrap_reference_base
600619
):
601-
textboxes_to_adjust: list[TextboxSizingArgs] = [
602-
{
603-
"base_text_layer": self.text_layer_rules_base,
604-
"base_textbox_reference": self.textbox_reference_base,
605-
"base_text_wrap_reference": self.text_wrap_reference_base,
606-
"divider_layer": self.divider_layer,
607-
"oracle_text": self.layout.oracle_text,
608-
"flavor_text": self.layout.flavor_text,
609-
}
610-
]
620+
if isinstance(self.layout, StationLayout):
621+
if self.station_group:
622+
self.station_group.visible = True
623+
624+
textbox_ref = self.station_levels_base_text_references[0]
625+
stations_len = len(self.layout.stations)
626+
627+
adjusted_text_layers: list[ArtLayer] = []
628+
629+
# Adjust Station levels
630+
for i in reversed(range(stations_len)):
631+
details = self.layout.stations[i]
632+
level_group = self.station_level_groups[i]
633+
level_text = self.station_level_text_layers[i]
634+
level_text_ref = self.station_levels_base_text_references[i]
635+
requirement_group = self.station_requirement_groups[i]
636+
pt_group = self.station_pt_groups[i]
637+
638+
sized = self.adjust_textbox_for_font_size(
639+
base_text_layer=level_text,
640+
base_textbox_reference=textbox_ref,
641+
base_text_wrap_reference=level_text_ref,
642+
divider_layer=None,
643+
oracle_text=details["ability"],
644+
flavor_text=None,
645+
)
611646

612-
if (
613-
isinstance(self.layout, AdventureLayout)
614-
and self.text_layer_rules_adventure
615-
and self.textbox_reference_adventure_base
616-
and self.text_wrap_reference_adventure_base
617-
):
618-
# With Adventure cards we need to make sure that
619-
# both left and right rules texts fit
620-
height_delta = self.textbox_reference_adventure_base.dims["height"]
621-
textboxes_to_adjust.append(
647+
if sized:
648+
text_layer = sized[0]
649+
650+
self.align_center_ys(requirement_group, text_layer)
651+
if "pt" in details:
652+
self.align_center_ys(pt_group, text_layer)
653+
654+
adjusted_text_layers.insert(0, text_layer)
655+
656+
text_ref = sized[1]
657+
next_ref = (
658+
self.textbox_reference_base.duplicate(
659+
self.textbox_reference_base,
660+
ElementPlacement.PlaceBefore,
661+
)
662+
if i == 0
663+
else self.station_levels_base_text_references[i - 1]
664+
)
665+
temp_shape = create_shape_layer(
666+
(
667+
{"x": 0, "y": text_ref.dims["top"]},
668+
{"x": self.doc_width, "y": text_ref.dims["top"]},
669+
{"x": self.doc_width, "y": self.doc_height},
670+
{"x": 0, "y": self.doc_height},
671+
),
672+
relative_layer=next_ref,
673+
placement=ElementPlacement.PlaceBefore,
674+
)
675+
676+
# Next rules text section needs to be placed on top of this one,
677+
# so we have to adjust it's text reference.
678+
textbox_ref = ReferenceLayer(
679+
merge_shapes(
680+
temp_shape,
681+
next_ref,
682+
operation=ShapeOperation.SubtractFront,
683+
)
684+
)
685+
textbox_ref.visible = False
686+
else:
687+
raise ValueError(
688+
f"Station textbox sizing failed for {level_group.name}"
689+
)
690+
691+
self.station_level_text_layers = adjusted_text_layers
692+
693+
# Adjust normal rules text
694+
if sized := self.adjust_textbox_for_font_size(
695+
base_text_layer=self.text_layer_rules_base,
696+
base_textbox_reference=textbox_ref,
697+
base_text_wrap_reference=textbox_ref,
698+
divider_layer=None,
699+
oracle_text=self.layout.oracle_text,
700+
flavor_text=None,
701+
):
702+
rules_text, textbox_ref, _ = sized
703+
self.text_layer_rules = rules_text
704+
ref = textbox_ref
705+
else:
706+
raise ValueError(
707+
"Station textbox sizing failed for normal rules text"
708+
)
709+
else:
710+
textboxes_to_adjust: list[TextboxSizingArgs] = [
622711
{
623-
"base_text_layer": self.text_layer_rules_adventure,
624-
"base_textbox_reference": self.textbox_reference_adventure_base,
625-
"base_text_wrap_reference": self.text_wrap_reference_adventure_base,
712+
"base_text_layer": self.text_layer_rules_base,
713+
"base_textbox_reference": self.textbox_reference_base,
714+
"base_text_wrap_reference": self.text_wrap_reference_base,
626715
"divider_layer": self.divider_layer,
627-
"oracle_text": self.layout.oracle_text_adventure,
628-
"flavor_text": self.layout.flavor_text_adventure,
629-
"height_padding": height_delta,
716+
"oracle_text": self.layout.oracle_text,
717+
"flavor_text": self.layout.flavor_text,
630718
}
631-
)
719+
]
632720

633-
# Adjust textboxes to fit rules text of fixed font size
634-
sized_boxes = self.adjust_textboxes_for_font_size(
635-
self.rules_text_font_size, textboxes_to_adjust
636-
)
721+
if (
722+
isinstance(self.layout, AdventureLayout)
723+
and self.text_layer_rules_adventure
724+
and self.textbox_reference_adventure_base
725+
and self.text_wrap_reference_adventure_base
726+
):
727+
# With Adventure cards we need to make sure that
728+
# both left and right rules texts fit
729+
height_delta = self.textbox_reference_adventure_base.dims["height"]
730+
textboxes_to_adjust.append(
731+
{
732+
"base_text_layer": self.text_layer_rules_adventure,
733+
"base_textbox_reference": self.textbox_reference_adventure_base,
734+
"base_text_wrap_reference": self.text_wrap_reference_adventure_base,
735+
"divider_layer": self.divider_layer,
736+
"oracle_text": self.layout.oracle_text_adventure,
737+
"flavor_text": self.layout.flavor_text_adventure,
738+
"height_padding": height_delta,
739+
}
740+
)
741+
742+
# Adjust textboxes to fit rules text of fixed font size
743+
sized_boxes = self.adjust_textboxes_for_font_size(
744+
self.rules_text_font_size, textboxes_to_adjust
745+
)
637746

638-
rules_text, textbox_ref, _ = sized_boxes[0]
639-
self.text_layer_rules = rules_text
640-
ref = textbox_ref
747+
rules_text, textbox_ref, _ = sized_boxes[0]
748+
self.text_layer_rules = rules_text
749+
ref = textbox_ref
641750

642-
if self.is_adventure:
643-
rules_text_left, textbox_ref_left, _ = sized_boxes[1]
751+
if self.is_adventure:
752+
rules_text_left, textbox_ref_left, _ = sized_boxes[1]
644753

645-
self.text_layer_rules_adventure = rules_text_left
646-
self.textbox_reference_adventure = textbox_ref_left
754+
self.text_layer_rules_adventure = rules_text_left
755+
self.textbox_reference_adventure = textbox_ref_left
647756
elif (
648757
not self.is_textless
649758
and self.supports_dynamic_textbox_height
@@ -1332,7 +1441,6 @@ def adjust_textboxes_for_font_size(
13321441
Adjusts multiple textboxes, whose bottom edges are aligned horizontally,
13331442
to font size so that all the textboxes will end up with the same height.
13341443
"""
1335-
doc_height: float | int = APP.activeDocument.height
13361444

13371445
# Size the textboxes starting from the one with most characters in its text
13381446
# in the hope that we avoid resizing textboxes that way.
@@ -1358,7 +1466,7 @@ def adjust_textboxes_for_font_size(
13581466
oracle_text=arg["oracle_text"],
13591467
flavor_text=arg["flavor_text"],
13601468
min_top=min(
1361-
arg.get("min_top", doc_height) or doc_height,
1469+
arg.get("min_top", self.doc_height) or self.doc_height,
13621470
tallest_top + height_padding,
13631471
)
13641472
or None,
@@ -1520,7 +1628,7 @@ def pw_enable_loyalty_graphics(self) -> None:
15201628
self.loyalty_group.visible = True
15211629

15221630
@cached_property
1523-
def text_layer_methods(self) -> list[Callable[[None], None]]:
1631+
def text_layer_methods(self) -> list[Callable[[], None]]:
15241632
methods = super().text_layer_methods
15251633
if not self.is_planeswalker:
15261634
methods.remove(self.pw_text_layers)
@@ -1530,7 +1638,7 @@ def text_layer_methods(self) -> list[Callable[[None], None]]:
15301638
return methods
15311639

15321640
@property
1533-
def post_text_methods(self) -> list[Callable[[None], None]]:
1641+
def post_text_methods(self) -> list[Callable[[], None]]:
15341642
methods = super().post_text_methods
15351643
methods.remove(self.pw_ability_mask)
15361644
if self.is_textless:
@@ -2023,7 +2131,7 @@ def text_layers_prototype(self) -> None:
20232131

20242132
def post_text_layers_prototype(self) -> None:
20252133
if isinstance(self.layout, PrototypeLayout):
2026-
if self.text_layer_rules_prototype:
2134+
if self.text_layer_rules_prototype and self.text_layer_rules:
20272135
self.text_layer_rules_prototype.textItem.size = (
20282136
self.text_layer_rules.textItem.size
20292137
)
@@ -2050,3 +2158,35 @@ def post_text_layers_prototype(self) -> None:
20502158
self.prototype_group.translate(0, delta)
20512159

20522160
# endregion Prototype
2161+
2162+
# region Station
2163+
2164+
@cached_property
2165+
def station_levels_base_text_references(self) -> list[ReferenceLayer]:
2166+
layers: list[ReferenceLayer] = []
2167+
if isinstance(self.layout, StationLayout):
2168+
for details, level_group in zip(
2169+
self.layout.stations, self.station_level_groups
2170+
):
2171+
if layer := get_reference_layer(
2172+
LAYER_NAMES.TEXT_REFERENCE_CREATURE
2173+
if "pt" in details
2174+
else LAYER_NAMES.TEXT_REFERENCE,
2175+
level_group,
2176+
):
2177+
layers.append(layer)
2178+
return layers
2179+
2180+
def frame_layers_station(self) -> None:
2181+
super().frame_layers_station()
2182+
2183+
for group_list in (self.station_requirement_groups, self.station_pt_groups):
2184+
for group in group_list:
2185+
if layer := getLayer(LAYERS.PINLINES, group):
2186+
self.generate_layer(group=layer, colors=self.pinlines_colors)
2187+
2188+
def layer_positioning_station(self) -> None:
2189+
if not self.rules_text_font_size:
2190+
return super().layer_positioning_station()
2191+
2192+
# endregion Station

py/helpers.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@ class LAYER_NAMES(StrEnum):
3939
REFERENCE = "Reference"
4040
REFERENCES = "References"
4141
TEXT_REFERENCE = "Text Reference"
42+
TEXT_REFERENCE_CREATURE = "Text Reference - Creature"
4243
OVERFLOW_REFERENCE = "Overflow Reference"
4344
UNIFIED = "Unified"
4445
FUSE = "Fuse"

0 commit comments

Comments
 (0)