fix(splash): skip runtime title/version draw and use mantle background
Some checks failed
Build and Test / build (pull_request) Failing after 19m15s
Some checks failed
Build and Test / build (pull_request) Failing after 19m15s
The splash PNG already has the title and version baked in by generate-splash.py, but SplashScreen::splashImage() was drawing them again at position (6,75) which fell outside the rounded background rectangle (starts at 30,30). Skip runtime text rendering for Kindred Create while keeping the dev build warning. Also switch the splash background from Catppuccin Mocha base (#1e1e2e) to mantle (#181825) for a darker appearance and regenerate assets.
This commit is contained in:
@@ -34,53 +34,67 @@ except ImportError:
|
|||||||
|
|
||||||
# Catppuccin Mocha colors
|
# Catppuccin Mocha colors
|
||||||
COLORS = {
|
COLORS = {
|
||||||
'base': '#1e1e2e',
|
"mantle": "#181825",
|
||||||
'surface0': '#313244',
|
"base": "#1e1e2e",
|
||||||
'text': '#cdd6f4',
|
"surface0": "#313244",
|
||||||
'subtext0': '#a6adc8',
|
"text": "#cdd6f4",
|
||||||
'blue': '#89b4fa',
|
"subtext0": "#a6adc8",
|
||||||
'lavender': '#b4befe',
|
"blue": "#89b4fa",
|
||||||
|
"lavender": "#b4befe",
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
def hex_to_rgb(hex_color):
|
def hex_to_rgb(hex_color):
|
||||||
"""Convert hex color to RGB tuple."""
|
"""Convert hex color to RGB tuple."""
|
||||||
hex_color = hex_color.lstrip('#')
|
hex_color = hex_color.lstrip("#")
|
||||||
return tuple(int(hex_color[i:i+2], 16) for i in (0, 2, 4))
|
return tuple(int(hex_color[i : i + 2], 16) for i in (0, 2, 4))
|
||||||
|
|
||||||
|
|
||||||
def create_rounded_rectangle(draw, bbox, radius, fill):
|
def create_rounded_rectangle(draw, bbox, radius, fill):
|
||||||
"""Draw a rounded rectangle."""
|
"""Draw a rounded rectangle."""
|
||||||
x1, y1, x2, y2 = bbox
|
x1, y1, x2, y2 = bbox
|
||||||
draw.rounded_rectangle(bbox, radius=radius, fill=fill)
|
draw.rounded_rectangle(bbox, radius=radius, fill=fill)
|
||||||
|
|
||||||
|
|
||||||
def load_svg_as_image(svg_path, width, height):
|
def load_svg_as_image(svg_path, width, height):
|
||||||
"""Load an SVG file and convert to PIL Image at specified size."""
|
"""Load an SVG file and convert to PIL Image at specified size."""
|
||||||
if cairosvg:
|
if cairosvg:
|
||||||
import io
|
import io
|
||||||
|
|
||||||
png_data = cairosvg.svg2png(url=str(svg_path), output_width=width, output_height=height)
|
png_data = cairosvg.svg2png(url=str(svg_path), output_width=width, output_height=height)
|
||||||
return Image.open(io.BytesIO(png_data)).convert('RGBA')
|
return Image.open(io.BytesIO(png_data)).convert("RGBA")
|
||||||
else:
|
else:
|
||||||
# Fallback: try using inkscape command line
|
# Fallback: try using inkscape command line
|
||||||
import subprocess
|
import subprocess
|
||||||
import tempfile
|
import tempfile
|
||||||
with tempfile.NamedTemporaryFile(suffix='.png', delete=False) as tmp:
|
|
||||||
|
with tempfile.NamedTemporaryFile(suffix=".png", delete=False) as tmp:
|
||||||
tmp_path = tmp.name
|
tmp_path = tmp.name
|
||||||
try:
|
try:
|
||||||
subprocess.run([
|
subprocess.run(
|
||||||
'inkscape', '-w', str(width), '-h', str(height),
|
["inkscape", "-w", str(width), "-h", str(height), str(svg_path), "-o", tmp_path],
|
||||||
str(svg_path), '-o', tmp_path
|
check=True,
|
||||||
], check=True, capture_output=True)
|
capture_output=True,
|
||||||
img = Image.open(tmp_path).convert('RGBA')
|
)
|
||||||
|
img = Image.open(tmp_path).convert("RGBA")
|
||||||
return img
|
return img
|
||||||
finally:
|
finally:
|
||||||
if os.path.exists(tmp_path):
|
if os.path.exists(tmp_path):
|
||||||
os.unlink(tmp_path)
|
os.unlink(tmp_path)
|
||||||
|
|
||||||
|
|
||||||
def get_font(size, bold=False):
|
def get_font(size, bold=False):
|
||||||
"""Get a font, falling back to default if not available."""
|
"""Get a font, falling back to default if not available."""
|
||||||
font_names = [
|
font_names = [
|
||||||
'/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf' if bold else '/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf',
|
"/usr/share/fonts/truetype/dejavu/DejaVuSans-Bold.ttf"
|
||||||
'/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf' if bold else '/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf',
|
if bold
|
||||||
'/usr/share/fonts/TTF/DejaVuSans-Bold.ttf' if bold else '/usr/share/fonts/TTF/DejaVuSans.ttf',
|
else "/usr/share/fonts/truetype/dejavu/DejaVuSans.ttf",
|
||||||
|
"/usr/share/fonts/truetype/liberation/LiberationSans-Bold.ttf"
|
||||||
|
if bold
|
||||||
|
else "/usr/share/fonts/truetype/liberation/LiberationSans-Regular.ttf",
|
||||||
|
"/usr/share/fonts/TTF/DejaVuSans-Bold.ttf"
|
||||||
|
if bold
|
||||||
|
else "/usr/share/fonts/TTF/DejaVuSans.ttf",
|
||||||
]
|
]
|
||||||
for font_name in font_names:
|
for font_name in font_names:
|
||||||
if os.path.exists(font_name):
|
if os.path.exists(font_name):
|
||||||
@@ -88,6 +102,7 @@ def get_font(size, bold=False):
|
|||||||
# Fallback to default
|
# Fallback to default
|
||||||
return ImageFont.load_default()
|
return ImageFont.load_default()
|
||||||
|
|
||||||
|
|
||||||
def create_splash(output_path, logo_path, width, height, version, freecad_version, scale=1):
|
def create_splash(output_path, logo_path, width, height, version, freecad_version, scale=1):
|
||||||
"""Create a splash screen image."""
|
"""Create a splash screen image."""
|
||||||
# Scale dimensions
|
# Scale dimensions
|
||||||
@@ -97,11 +112,11 @@ def create_splash(output_path, logo_path, width, height, version, freecad_versio
|
|||||||
padding = int(30 * scale)
|
padding = int(30 * scale)
|
||||||
|
|
||||||
# Create image with transparent background
|
# Create image with transparent background
|
||||||
img = Image.new('RGBA', (w, h), (0, 0, 0, 0))
|
img = Image.new("RGBA", (w, h), (0, 0, 0, 0))
|
||||||
draw = ImageDraw.Draw(img)
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
# Draw rounded rectangle background
|
# Draw rounded rectangle background
|
||||||
bg_color = hex_to_rgb(COLORS['base'])
|
bg_color = hex_to_rgb(COLORS["mantle"])
|
||||||
create_rounded_rectangle(draw, (padding, padding, w - padding, h - padding), radius, bg_color)
|
create_rounded_rectangle(draw, (padding, padding, w - padding, h - padding), radius, bg_color)
|
||||||
|
|
||||||
# Load and place logo
|
# Load and place logo
|
||||||
@@ -125,7 +140,7 @@ def create_splash(output_path, logo_path, width, height, version, freecad_versio
|
|||||||
title_width = title_bbox[2] - title_bbox[0]
|
title_width = title_bbox[2] - title_bbox[0]
|
||||||
title_x = (w - title_width) // 2
|
title_x = (w - title_width) // 2
|
||||||
title_y = int(250 * scale)
|
title_y = int(250 * scale)
|
||||||
draw.text((title_x, title_y), title, fill=hex_to_rgb(COLORS['text']), font=title_font)
|
draw.text((title_x, title_y), title, fill=hex_to_rgb(COLORS["text"]), font=title_font)
|
||||||
|
|
||||||
# Draw version string
|
# Draw version string
|
||||||
version_font = get_font(int(12 * scale))
|
version_font = get_font(int(12 * scale))
|
||||||
@@ -134,12 +149,15 @@ def create_splash(output_path, logo_path, width, height, version, freecad_versio
|
|||||||
version_width = version_bbox[2] - version_bbox[0]
|
version_width = version_bbox[2] - version_bbox[0]
|
||||||
version_x = (w - version_width) // 2
|
version_x = (w - version_width) // 2
|
||||||
version_y = title_y + int(35 * scale)
|
version_y = title_y + int(35 * scale)
|
||||||
draw.text((version_x, version_y), version_str, fill=hex_to_rgb(COLORS['subtext0']), font=version_font)
|
draw.text(
|
||||||
|
(version_x, version_y), version_str, fill=hex_to_rgb(COLORS["subtext0"]), font=version_font
|
||||||
|
)
|
||||||
|
|
||||||
# Save
|
# Save
|
||||||
img.save(output_path, 'PNG')
|
img.save(output_path, "PNG")
|
||||||
print(f"Created: {output_path}")
|
print(f"Created: {output_path}")
|
||||||
|
|
||||||
|
|
||||||
def create_about(output_path, logo_path, width, height, scale=1):
|
def create_about(output_path, logo_path, width, height, scale=1):
|
||||||
"""Create an about dialog image."""
|
"""Create an about dialog image."""
|
||||||
# Scale dimensions
|
# Scale dimensions
|
||||||
@@ -147,7 +165,7 @@ def create_about(output_path, logo_path, width, height, scale=1):
|
|||||||
h = int(height * scale)
|
h = int(height * scale)
|
||||||
|
|
||||||
# Create image
|
# Create image
|
||||||
img = Image.new('RGBA', (w, h), hex_to_rgb(COLORS['base']) + (255,))
|
img = Image.new("RGBA", (w, h), hex_to_rgb(COLORS["base"]) + (255,))
|
||||||
draw = ImageDraw.Draw(img)
|
draw = ImageDraw.Draw(img)
|
||||||
|
|
||||||
# Load and place logo
|
# Load and place logo
|
||||||
@@ -163,18 +181,19 @@ def create_about(output_path, logo_path, width, height, scale=1):
|
|||||||
print(f"Warning: Could not load logo: {e}")
|
print(f"Warning: Could not load logo: {e}")
|
||||||
|
|
||||||
# Save
|
# Save
|
||||||
img.save(output_path, 'PNG')
|
img.save(output_path, "PNG")
|
||||||
print(f"Created: {output_path}")
|
print(f"Created: {output_path}")
|
||||||
|
|
||||||
|
|
||||||
def main():
|
def main():
|
||||||
parser = argparse.ArgumentParser(description='Generate Kindred Create splash screens')
|
parser = argparse.ArgumentParser(description="Generate Kindred Create splash screens")
|
||||||
parser.add_argument('--version', default='0.1.0', help='Kindred Create version')
|
parser.add_argument("--version", default="0.1.0", help="Kindred Create version")
|
||||||
parser.add_argument('--freecad-version', default='1.0.0', help='FreeCAD version')
|
parser.add_argument("--freecad-version", default="1.0.0", help="FreeCAD version")
|
||||||
args = parser.parse_args()
|
args = parser.parse_args()
|
||||||
|
|
||||||
script_dir = Path(__file__).parent
|
script_dir = Path(__file__).parent
|
||||||
logo_path = script_dir / 'kindred-logo.svg'
|
logo_path = script_dir / "kindred-logo.svg"
|
||||||
icons_dir = script_dir.parent.parent / 'src' / 'Gui' / 'Icons'
|
icons_dir = script_dir.parent.parent / "src" / "Gui" / "Icons"
|
||||||
|
|
||||||
if not logo_path.exists():
|
if not logo_path.exists():
|
||||||
print(f"Error: Logo not found at {logo_path}")
|
print(f"Error: Logo not found at {logo_path}")
|
||||||
@@ -182,33 +201,31 @@ def main():
|
|||||||
|
|
||||||
# Create splash screens (600x400 as per spec)
|
# Create splash screens (600x400 as per spec)
|
||||||
create_splash(
|
create_splash(
|
||||||
icons_dir / 'kindredcreatesplash.png',
|
icons_dir / "kindredcreatesplash.png",
|
||||||
logo_path,
|
logo_path,
|
||||||
600, 400,
|
600,
|
||||||
|
400,
|
||||||
args.version,
|
args.version,
|
||||||
args.freecad_version,
|
args.freecad_version,
|
||||||
scale=1
|
scale=1,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create 2x version for HiDPI
|
# Create 2x version for HiDPI
|
||||||
create_splash(
|
create_splash(
|
||||||
icons_dir / 'kindredcreatesplash_2x.png',
|
icons_dir / "kindredcreatesplash_2x.png",
|
||||||
logo_path,
|
logo_path,
|
||||||
600, 400,
|
600,
|
||||||
|
400,
|
||||||
args.version,
|
args.version,
|
||||||
args.freecad_version,
|
args.freecad_version,
|
||||||
scale=2
|
scale=2,
|
||||||
)
|
)
|
||||||
|
|
||||||
# Create about image
|
# Create about image
|
||||||
create_about(
|
create_about(icons_dir / "kindredcreateabout.png", logo_path, 400, 200, scale=1)
|
||||||
icons_dir / 'kindredcreateabout.png',
|
|
||||||
logo_path,
|
|
||||||
400, 200,
|
|
||||||
scale=1
|
|
||||||
)
|
|
||||||
|
|
||||||
print("\nSplash screen generation complete!")
|
print("\nSplash screen generation complete!")
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
|
if __name__ == "__main__":
|
||||||
main()
|
main()
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 11 KiB |
Binary file not shown.
|
Before Width: | Height: | Size: 25 KiB After Width: | Height: | Size: 24 KiB |
@@ -360,9 +360,6 @@ QPixmap SplashScreen::splashImage()
|
|||||||
fontExe.setPointSizeF(20.0);
|
fontExe.setPointSizeF(20.0);
|
||||||
QFontMetrics metricExe(fontExe);
|
QFontMetrics metricExe(fontExe);
|
||||||
int l = QtTools::horizontalAdvance(metricExe, title);
|
int l = QtTools::horizontalAdvance(metricExe, title);
|
||||||
if (title == QLatin1String("Kindred Create")) {
|
|
||||||
// For Kindred Create splash, we draw the title as part of the dynamic rendering
|
|
||||||
}
|
|
||||||
int w = splash_image.width();
|
int w = splash_image.width();
|
||||||
int h = splash_image.height();
|
int h = splash_image.height();
|
||||||
|
|
||||||
@@ -386,6 +383,8 @@ QPixmap SplashScreen::splashImage()
|
|||||||
QColor color(QString::fromStdString(tc->second));
|
QColor color(QString::fromStdString(tc->second));
|
||||||
if (color.isValid()) {
|
if (color.isValid()) {
|
||||||
painter.setPen(color);
|
painter.setPen(color);
|
||||||
|
if (title != QLatin1String("Kindred Create")) {
|
||||||
|
// Kindred Create's splash PNG already contains the title and version
|
||||||
painter.setFont(fontExe);
|
painter.setFont(fontExe);
|
||||||
if (title != QLatin1String("FreeCAD")) {
|
if (title != QLatin1String("FreeCAD")) {
|
||||||
// FreeCAD's Splashscreen already contains the EXE name, no need to draw it
|
// FreeCAD's Splashscreen already contains the EXE name, no need to draw it
|
||||||
@@ -393,6 +392,7 @@ QPixmap SplashScreen::splashImage()
|
|||||||
}
|
}
|
||||||
painter.setFont(fontVer);
|
painter.setFont(fontVer);
|
||||||
painter.drawText(x + (l + 235), y - 7, version);
|
painter.drawText(x + (l + 235), y - 7, version);
|
||||||
|
}
|
||||||
QColor warningColor(QString::fromStdString(wc->second));
|
QColor warningColor(QString::fromStdString(wc->second));
|
||||||
if (suffix == QLatin1String("dev") && warningColor.isValid()) {
|
if (suffix == QLatin1String("dev") && warningColor.isValid()) {
|
||||||
fontVer.setPointSizeF(14.0);
|
fontVer.setPointSizeF(14.0);
|
||||||
|
|||||||
Reference in New Issue
Block a user