DazedTL/gui/platform_glyph.py
2026-06-13 18:29:07 -05:00

161 lines
5.8 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""Linux fallbacks for emoji that PyQt5 often cannot render (tofu boxes).
Only replaces glyphs known to fail on typical Linux font stacks.
Windows/macOS get the original strings unchanged.
"""
from __future__ import annotations
import re
import sys
from PyQt5.QtCore import Qt, QSize
from PyQt5.QtGui import QColor, QFont, QIcon, QPainter, QPixmap
# Toolbar buttons, section headers, mixed labels
_LINUX_EMOJI_FALLBACKS: dict[str, str] = {
"\U00002795": "+", # add
"\U0001F4C1": "\u2302", # 📁 folder → ⌂
"\U0001F4C2": "\u2302", # 📂 open folder
"\U0001F504": "\u21bb", # 🔄 refresh → ↻
"\U0001F4DD": "", # 📝 memo (section headers)
"\U0001F6D1": "\u25a0", # 🛑 stop → ■
"\U0001F4E5": "\u2193", # 📥 inbox tray → ↓
"\U0001F527": "\u2699", # 🔧 wrench → ⚙
"\U0001F511": "", # 🔑 key (section headers)
"\U0001F310": "", # 🌐 globe (section headers)
"\U0001F5A5": "", # 🖥 desktop (section headers)
"\U00002699\uFE0F": "\u2699", # ⚙️ → ⚙
"\U0000270F\uFE0F": "\u270E", # ✏️ → ✎
}
# Icon-only nav tabs — never empty; BMP symbols so metrics match across the bar
_LINUX_NAV_FALLBACKS: dict[str, str] = {
"\U0001F310": "\u25ce", # 🌐 Translation → ◎
"\U0001F527": "\u2699", # 🔧 General → ⚙
"\U0001F3AE": "\u25c6", # 🎮 MV/MZ → ◆
"\U0001F43A": "\u25c8", # 🐺 Wolf → ◈
"\U0001F4C4": "\u2630", # 📄 CSV → ☰
"\U00002694\uFE0F": "\u2694", # ⚔️ SRPG → ⚔
"\U00002694": "\u2694",
"\U0001F504": "\u21bb", # 🔄 Update → ↻
"\U00002699\uFE0F": "\u2699", # ⚙️ Config → ⚙
"\U000026A1": "\u26a1", # ⚡ Workflow
}
def _apply_fallbacks(text: str, mapping: dict[str, str]) -> str:
out = text
for emoji, replacement in mapping.items():
out = out.replace(emoji, replacement)
out = re.sub(r" +", " ", out)
return out.strip()
def platform_glyph(text: str) -> str:
"""Return UI text with unsupported emoji swapped on Linux only."""
if not sys.platform.startswith("linux") or not text:
return text
return _apply_fallbacks(text, _LINUX_EMOJI_FALLBACKS)
def platform_nav_glyph(icon: str) -> str:
"""Linux fallback for icon-only nav tabs (never returns empty)."""
if not sys.platform.startswith("linux") or not icon:
return icon
out = _apply_fallbacks(icon, _LINUX_NAV_FALLBACKS)
if not out:
out = _apply_fallbacks(icon, _LINUX_EMOJI_FALLBACKS)
return out or icon
def _linux_nav_pixmap(glyph: str, *, size: int, font_px: int, color: QColor) -> QPixmap:
pixmap = QPixmap(size, size)
pixmap.fill(Qt.transparent)
painter = QPainter(pixmap)
font = QFont("DejaVu Sans")
font.setPixelSize(font_px)
painter.setFont(font)
painter.setPen(color)
painter.drawText(pixmap.rect(), Qt.AlignCenter, glyph)
painter.end()
return pixmap
def _linux_nav_icon(icon_text: str, *, size: int, font_px: int) -> QIcon:
"""Render a nav glyph into a fixed-size pixmap so every tab aligns the same."""
glyph = platform_nav_glyph(icon_text)
icon = QIcon()
gray = _linux_nav_pixmap(glyph, size=size, font_px=font_px, color=QColor("#cccccc"))
white = _linux_nav_pixmap(glyph, size=size, font_px=font_px, color=QColor("#ffffff"))
icon.addPixmap(gray, QIcon.Normal, QIcon.Off)
icon.addPixmap(gray, QIcon.Disabled, QIcon.Off)
icon.addPixmap(white, QIcon.Active, QIcon.Off)
icon.addPixmap(white, QIcon.Selected, QIcon.Off)
return icon
def _nav_toolbutton_stylesheet(*, horizontal: bool, icon_only: bool) -> str:
border_prop = "border-bottom" if horizontal else "border-left"
if icon_only:
return f"""
QToolButton {{
background-color: transparent;
border: none;
{border_prop}: 3px solid transparent;
color: #cccccc;
padding: 0px;
margin: 0px;
}}
QToolButton:hover {{
background-color: #3e3e42;
}}
QToolButton:checked {{
background-color: #37373d;
{border_prop}: 3px solid #007acc;
color: #ffffff;
}}
"""
font_size = "18px" if sys.platform.startswith("linux") else "24px"
return f"""
QToolButton {{
background-color: transparent;
border: none;
{border_prop}: 3px solid transparent;
color: #cccccc;
font-size: {font_size};
font-family: 'DejaVu Sans Mono', monospace;
padding: 0px;
margin: 0px;
}}
QToolButton:hover {{
background-color: #3e3e42;
}}
QToolButton:checked {{
background-color: #37373d;
{border_prop}: 3px solid #007acc;
color: #ffffff;
}}
"""
def configure_nav_toolbutton(btn, icon_text: str, *, horizontal: bool = False) -> None:
"""Apply nav icon — on Linux render BMP symbols into fixed pixmaps for alignment."""
if sys.platform.startswith("linux"):
# Scale to the button cell (leave room for the 3px active border).
icon_dim = max(28, min(btn.width(), btn.height()) - 8)
font_px = max(22, int(icon_dim * 0.78))
btn.setIcon(_linux_nav_icon(icon_text, size=icon_dim, font_px=font_px))
btn.setIconSize(QSize(icon_dim, icon_dim))
btn.setText("")
btn.setToolButtonStyle(Qt.ToolButtonIconOnly)
btn.setStyleSheet(_nav_toolbutton_stylesheet(horizontal=horizontal, icon_only=True))
return
btn.setIcon(QIcon())
btn.setText(icon_text)
btn.setToolButtonStyle(Qt.ToolButtonTextOnly)
btn.setStyleSheet(_nav_toolbutton_stylesheet(horizontal=horizontal, icon_only=False))