ui(brand): superimposed dragon watermark behind participants, theme-flipped (white/dark, black/light)
Some checks failed
CI / build-and-test (push) Failing after 27s
Some checks failed
CI / build-and-test (push) Failing after 27s
This commit is contained in:
parent
80d9baf2d0
commit
dfdfa9e0e1
7 changed files with 99 additions and 2 deletions
34
src/TeamsISO.App/Assets/_recolor_dragon.py
Normal file
34
src/TeamsISO.App/Assets/_recolor_dragon.py
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
from PIL import Image
|
||||||
|
import os
|
||||||
|
|
||||||
|
# We treat the navy-blue dragon-mark.png as a silhouette source: anything with
|
||||||
|
# nontrivial alpha is "dragon", everything else stays transparent. We emit a
|
||||||
|
# pure-black and pure-white variant, tightly cropped to the actual content
|
||||||
|
# bbox so they center cleanly when used as a watermark.
|
||||||
|
ROOT = os.path.dirname(os.path.abspath(__file__))
|
||||||
|
src_path = os.path.join(ROOT, "dragon-mark.png")
|
||||||
|
src = Image.open(src_path).convert("RGBA")
|
||||||
|
|
||||||
|
alpha = src.split()[-1]
|
||||||
|
# Threshold to drop anti-alias fringe that can fool getbbox into reporting
|
||||||
|
# the whole canvas as "in".
|
||||||
|
mask = alpha.point(lambda v: 255 if v > 16 else 0)
|
||||||
|
bbox = mask.getbbox()
|
||||||
|
print("content bbox =", bbox, "size =", (bbox[2] - bbox[0], bbox[3] - bbox[1]))
|
||||||
|
|
||||||
|
cropped = src.crop(bbox)
|
||||||
|
_, _, _, ca = cropped.split()
|
||||||
|
|
||||||
|
for name, rgb in (("black", (0, 0, 0)), ("white", (255, 255, 255))):
|
||||||
|
flat = Image.merge(
|
||||||
|
"RGBA",
|
||||||
|
(
|
||||||
|
Image.new("L", cropped.size, rgb[0]),
|
||||||
|
Image.new("L", cropped.size, rgb[1]),
|
||||||
|
Image.new("L", cropped.size, rgb[2]),
|
||||||
|
ca,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
out_path = os.path.join(ROOT, f"dragon-mark-{name}.png")
|
||||||
|
flat.save(out_path, "PNG", optimize=True)
|
||||||
|
print("wrote", out_path, flat.size)
|
||||||
BIN
src/TeamsISO.App/Assets/dragon-mark-black.png
Normal file
BIN
src/TeamsISO.App/Assets/dragon-mark-black.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
BIN
src/TeamsISO.App/Assets/dragon-mark-white.png
Normal file
BIN
src/TeamsISO.App/Assets/dragon-mark-white.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 24 KiB |
|
|
@ -106,7 +106,11 @@
|
||||||
BorderThickness="0"
|
BorderThickness="0"
|
||||||
Cursor="Hand"
|
Cursor="Hand"
|
||||||
ToolTip="About TeamsISO">
|
ToolTip="About TeamsISO">
|
||||||
<Image Source="/Assets/dragon-mark.png"
|
<!-- Source bound to Wd.BrandMark.Image so the mark flips
|
||||||
|
white↔black with the active theme (see Theme.Dark /
|
||||||
|
Theme.Light). The PNG carries its own AA so HighQuality
|
||||||
|
scaling is preferred over NearestNeighbor at this size. -->
|
||||||
|
<Image Source="{DynamicResource Wd.BrandMark.Image}"
|
||||||
Width="20" Height="20"
|
Width="20" Height="20"
|
||||||
RenderOptions.BitmapScalingMode="HighQuality"/>
|
RenderOptions.BitmapScalingMode="HighQuality"/>
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -447,8 +451,30 @@
|
||||||
BorderBrush="{DynamicResource Wd.Border}"
|
BorderBrush="{DynamicResource Wd.Border}"
|
||||||
BorderThickness="1"
|
BorderThickness="1"
|
||||||
CornerRadius="{StaticResource Radius.M}"
|
CornerRadius="{StaticResource Radius.M}"
|
||||||
Background="{DynamicResource Wd.Surface}">
|
Background="{DynamicResource Wd.Surface}"
|
||||||
|
ClipToBounds="True">
|
||||||
<Grid>
|
<Grid>
|
||||||
|
<!--
|
||||||
|
Brand watermark superimposed BEHIND the participants grid.
|
||||||
|
Sits at 6% opacity so a populated grid reads cleanly over
|
||||||
|
the top while the dragon is still visible through the
|
||||||
|
transparent row backgrounds (RowBackground="Transparent"
|
||||||
|
on the DataGrid below). When the grid is empty the
|
||||||
|
watermark becomes the de-facto empty-state surface.
|
||||||
|
|
||||||
|
IsHitTestVisible=False so the watermark never absorbs
|
||||||
|
clicks meant for grid rows or the empty area below them.
|
||||||
|
Source binds to the theme-flipped Wd.BrandMark.Image
|
||||||
|
resource — white dragon in dark mode, black in light.
|
||||||
|
-->
|
||||||
|
<Image Source="{DynamicResource Wd.BrandMark.Image}"
|
||||||
|
Opacity="0.06"
|
||||||
|
Stretch="Uniform"
|
||||||
|
HorizontalAlignment="Center"
|
||||||
|
VerticalAlignment="Center"
|
||||||
|
Margin="40"
|
||||||
|
IsHitTestVisible="False"
|
||||||
|
RenderOptions.BitmapScalingMode="HighQuality"/>
|
||||||
<DataGrid x:Name="ParticipantsGrid"
|
<DataGrid x:Name="ParticipantsGrid"
|
||||||
ItemsSource="{Binding ParticipantsView}"
|
ItemsSource="{Binding ParticipantsView}"
|
||||||
AutoGenerateColumns="False"
|
AutoGenerateColumns="False"
|
||||||
|
|
|
||||||
|
|
@ -60,7 +60,17 @@
|
||||||
|
|
||||||
<!-- Wild Dragon brand assets — embedded as resources so the published binary is self-contained. -->
|
<!-- Wild Dragon brand assets — embedded as resources so the published binary is self-contained. -->
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
|
<!-- Source navy-blue dragon-mark, kept for AboutWindow / installer iconography. -->
|
||||||
<Resource Include="Assets\dragon-mark.png" />
|
<Resource Include="Assets\dragon-mark.png" />
|
||||||
|
<!--
|
||||||
|
Theme-aware silhouette variants used by Theme.Dark / Theme.Light to expose
|
||||||
|
a single Wd.BrandMark.Image resource key. The dark theme picks the white
|
||||||
|
dragon (visible on #0A0A0A), the light theme picks the black dragon
|
||||||
|
(visible on #FAFAFB). Generated from dragon-mark.png via
|
||||||
|
Assets/_recolor_dragon.py — re-run if the source mark ever changes.
|
||||||
|
-->
|
||||||
|
<Resource Include="Assets\dragon-mark-white.png" />
|
||||||
|
<Resource Include="Assets\dragon-mark-black.png" />
|
||||||
<Resource Include="Assets\wild-dragon-wordmark.png" />
|
<Resource Include="Assets\wild-dragon-wordmark.png" />
|
||||||
<Resource Include="Assets\teamsiso.ico" />
|
<Resource Include="Assets\teamsiso.ico" />
|
||||||
<!--
|
<!--
|
||||||
|
|
|
||||||
|
|
@ -44,4 +44,20 @@
|
||||||
<SolidColorBrush x:Key="Wd.Status.LiveBg" Color="#13261A"/>
|
<SolidColorBrush x:Key="Wd.Status.LiveBg" Color="#13261A"/>
|
||||||
<SolidColorBrush x:Key="Wd.Status.Warn" Color="#FBBF24"/>
|
<SolidColorBrush x:Key="Wd.Status.Warn" Color="#FBBF24"/>
|
||||||
<SolidColorBrush x:Key="Wd.Status.Error" Color="#FB819C"/>
|
<SolidColorBrush x:Key="Wd.Status.Error" Color="#FB819C"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Brand mark image, theme-flipped. Dark mode shows the WHITE dragon so it
|
||||||
|
reads against the near-black canvas. The light theme exposes the same
|
||||||
|
key pointing at the BLACK dragon. Consumers bind via
|
||||||
|
{DynamicResource Wd.BrandMark.Image} so the swap is automatic on
|
||||||
|
ThemeManager.Toggle().
|
||||||
|
|
||||||
|
CacheOption=OnLoad decodes the PNG at load time and releases the
|
||||||
|
underlying stream, which matters because the source files are 1243×1125
|
||||||
|
— without OnLoad the BitmapImage holds the stream open for the life
|
||||||
|
of the resource dictionary.
|
||||||
|
-->
|
||||||
|
<BitmapImage x:Key="Wd.BrandMark.Image"
|
||||||
|
UriSource="pack://application:,,,/TeamsISO;component/Assets/dragon-mark-white.png"
|
||||||
|
CacheOption="OnLoad"/>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|
|
||||||
|
|
@ -46,4 +46,15 @@
|
||||||
<SolidColorBrush x:Key="Wd.Status.LiveBg" Color="#DCFCE7"/>
|
<SolidColorBrush x:Key="Wd.Status.LiveBg" Color="#DCFCE7"/>
|
||||||
<SolidColorBrush x:Key="Wd.Status.Warn" Color="#B45309"/>
|
<SolidColorBrush x:Key="Wd.Status.Warn" Color="#B45309"/>
|
||||||
<SolidColorBrush x:Key="Wd.Status.Error" Color="#D43E5C"/>
|
<SolidColorBrush x:Key="Wd.Status.Error" Color="#D43E5C"/>
|
||||||
|
|
||||||
|
<!--
|
||||||
|
Brand mark image, theme-flipped. Light mode shows the BLACK dragon so it
|
||||||
|
reads against the cyan-tinted off-white canvas. Mirror of the Dark
|
||||||
|
theme's resource — same key, opposite silhouette. Consumers use
|
||||||
|
{DynamicResource Wd.BrandMark.Image} so the swap is automatic.
|
||||||
|
See Theme.Dark.xaml's comment for the CacheOption=OnLoad rationale.
|
||||||
|
-->
|
||||||
|
<BitmapImage x:Key="Wd.BrandMark.Image"
|
||||||
|
UriSource="pack://application:,,,/TeamsISO;component/Assets/dragon-mark-black.png"
|
||||||
|
CacheOption="OnLoad"/>
|
||||||
</ResourceDictionary>
|
</ResourceDictionary>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue