v0.31.2: panels pushed in, buttons pushed out; black bpm text
- The now-playing panel and the visualizer are debossed by exactly as much as the buttons are embossed: the same rims and outline, flipped (theme.deboss_qss / theme.paint_deboss), so things you press stand out and things that show you something sit in. - The bpm button's text is black, enabled or not. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -1,3 +1,3 @@
|
||||
"""LinTunes — iTunes-style music library manager and player for Linux."""
|
||||
|
||||
__version__ = "0.31.1"
|
||||
__version__ = "0.31.2"
|
||||
|
||||
@@ -51,6 +51,9 @@ TILE_STYLE = """
|
||||
QFrame#transportBox QToolButton, QFrame#transportBox QPushButton {
|
||||
border: none; background: transparent; border-radius: 6px;
|
||||
}
|
||||
QFrame#transportBox QPushButton, QFrame#transportBox QPushButton:disabled {
|
||||
color: black;
|
||||
}
|
||||
QFrame#transportBox[tiles="true"] QToolButton:hover {
|
||||
background: rgba(0, 0, 0, 22);
|
||||
}
|
||||
@@ -61,12 +64,8 @@ QFrame#transportBox[tiles="true"] QToolButton:pressed {
|
||||
|
||||
|
||||
def _nowplaying_style(bg: QColor) -> str:
|
||||
return f"""
|
||||
QFrame#nowPlayingBox {{
|
||||
background: {bg.name()};
|
||||
border-radius: 8px;
|
||||
}}
|
||||
"""
|
||||
"""Debossed: pushed in as far as the buttons stand out."""
|
||||
return theme.deboss_qss("QFrame#nowPlayingBox", bg, radius=8)
|
||||
|
||||
# Title sits this many points above the artist/album line, both following the
|
||||
# UI-scale app font (see theme.UI_SCALES).
|
||||
|
||||
@@ -211,13 +211,11 @@ class VisualizerWidget(QWidget):
|
||||
def paintEvent(self, event):
|
||||
painter = QPainter(self)
|
||||
painter.setRenderHint(QPainter.RenderHint.Antialiasing)
|
||||
# Rounded gray panel + 1px border, matching the transport button boxes.
|
||||
# A rounded panel debossed as far as the transport buttons stand out:
|
||||
# it shows things, it isn't pressed.
|
||||
rect = QRectF(self.rect()).adjusted(0.5, 0.5, -0.5, -0.5)
|
||||
panel = QPainterPath()
|
||||
panel.addRoundedRect(rect, RADIUS, RADIUS)
|
||||
painter.fillPath(panel, self.palette().alternateBase().color())
|
||||
painter.setPen(QPen(self.palette().mid().color(), 1))
|
||||
painter.drawPath(panel)
|
||||
panel = theme.paint_deboss(painter, rect,
|
||||
self.palette().alternateBase().color(), RADIUS)
|
||||
painter.setClipPath(panel) # keep bars inside the rounded shape
|
||||
if self._no_pcm:
|
||||
# Casting: the audio never passes through this machine, so show
|
||||
|
||||
+55
-14
@@ -165,25 +165,27 @@ def apply_theme(app, prefs):
|
||||
""")
|
||||
|
||||
|
||||
def emboss_gradient(color: QColor, pressed: bool = False) -> str:
|
||||
"""A shallow emboss around ``color`` that hugs the edges: a crisp lit rim
|
||||
along the top and a crisp shaded rim along the bottom, with the face
|
||||
itself nearly flat. Computed from the live color, so it follows the
|
||||
palette (light or dark) and the user's grayscale overrides. ``pressed``
|
||||
flips it, which reads as the button sinking in.
|
||||
|
||||
The color itself sits at the midpoint, so what the user picked is still
|
||||
exactly what the middle of the button shows."""
|
||||
def emboss_stops(color: QColor, pressed: bool = False) -> list:
|
||||
"""[(position, QColor)] of the emboss: a crisp lit rim along the top and
|
||||
a crisp shaded rim along the bottom, with the face itself nearly flat.
|
||||
``pressed`` flips it — a button sinking in, or a debossed panel. The
|
||||
color itself sits at the midpoint, so what the user picked is still
|
||||
exactly what the middle shows."""
|
||||
rim_top, face_top = color.lighter(EMBOSS_RIM), color.lighter(EMBOSS_LIGHTER)
|
||||
face_bottom, rim_bottom = color.darker(EMBOSS_DARKER), color.darker(EMBOSS_RIM)
|
||||
if pressed:
|
||||
rim_top, face_top, face_bottom, rim_bottom = (
|
||||
rim_bottom, face_bottom, face_top, rim_top)
|
||||
return (f"qlineargradient(x1:0, y1:0, x2:0, y2:1, "
|
||||
f"stop:0 {rim_top.name()}, stop:{EMBOSS_RIM_WIDTH} {face_top.name()}, "
|
||||
f"stop:0.5 {color.name()}, "
|
||||
f"stop:{1 - EMBOSS_RIM_WIDTH:.2f} {face_bottom.name()}, "
|
||||
f"stop:1 {rim_bottom.name()})")
|
||||
return [(0, rim_top), (EMBOSS_RIM_WIDTH, face_top), (0.5, color),
|
||||
(round(1 - EMBOSS_RIM_WIDTH, 2), face_bottom), (1, rim_bottom)]
|
||||
|
||||
|
||||
def emboss_gradient(color: QColor, pressed: bool = False) -> str:
|
||||
"""``emboss_stops`` as a style-sheet gradient. Computed from the live
|
||||
color, so it follows the palette and the user's grayscale overrides."""
|
||||
stops = ", ".join(f"stop:{pos} {c.name()}"
|
||||
for pos, c in emboss_stops(color, pressed))
|
||||
return f"qlineargradient(x1:0, y1:0, x2:0, y2:1, {stops})"
|
||||
|
||||
|
||||
# How far the emboss strays from the base color (Qt's lighter/darker factor,
|
||||
@@ -203,6 +205,45 @@ def emboss_edges(color: QColor) -> str:
|
||||
return f"border: 1px solid {edge}; border-bottom-color: {bottom};"
|
||||
|
||||
|
||||
def deboss_edges(color: QColor) -> str:
|
||||
"""The emboss outline turned over: darker along the top, as a panel set
|
||||
into the surface catches shadow there."""
|
||||
edge = color.darker(135).name()
|
||||
top = color.darker(155).name()
|
||||
return f"border: 1px solid {edge}; border-top-color: {top};"
|
||||
|
||||
|
||||
def deboss_qss(selector: str, color: QColor, *, radius: int = 8,
|
||||
extra: str = "") -> str:
|
||||
"""Pushed in, by exactly as much as a button is pushed out: the same
|
||||
rims and outline, flipped. For panels that show things rather than do
|
||||
things (the now-playing panel, the visualizer)."""
|
||||
return (f"{selector} {{ {deboss_edges(color)} border-radius: {radius}px;"
|
||||
f" background: {emboss_gradient(color, pressed=True)}; {extra} }}")
|
||||
|
||||
|
||||
def paint_deboss(painter, rect, color: QColor, radius: float):
|
||||
"""``deboss_qss`` for a widget that paints itself: fills ``rect`` (a
|
||||
QRectF) as a debossed rounded panel and returns its path, for clipping."""
|
||||
from PyQt6.QtCore import QPointF
|
||||
from PyQt6.QtGui import QLinearGradient, QPainterPath, QPen
|
||||
path = QPainterPath()
|
||||
path.addRoundedRect(rect, radius, radius)
|
||||
gradient = QLinearGradient(QPointF(0, rect.top()), QPointF(0, rect.bottom()))
|
||||
for pos, stop in emboss_stops(color, pressed=True):
|
||||
gradient.setColorAt(pos, stop)
|
||||
painter.fillPath(path, gradient)
|
||||
painter.setPen(QPen(color.darker(135), 1))
|
||||
painter.drawPath(path)
|
||||
# the darker top edge
|
||||
painter.save()
|
||||
painter.setClipRect(rect.adjusted(-1, -1, 1, -rect.height() / 2))
|
||||
painter.setPen(QPen(color.darker(155), 1))
|
||||
painter.drawPath(path)
|
||||
painter.restore()
|
||||
return path
|
||||
|
||||
|
||||
def button_face_qss(selector: str, color: QColor, *, radius: int = 4,
|
||||
extra: str = "", checked_color: QColor | None = None,
|
||||
checked_text: str = "") -> str:
|
||||
|
||||
Reference in New Issue
Block a user