Skip to content

Commit

Permalink
Minor improvements (Focus Indicator)
Browse files Browse the repository at this point in the history
Focus indicators added to base button and base line edit widgets.
Minor improvements.
  • Loading branch information
VasigaranAndAngel committed Oct 26, 2024
1 parent 45c8aa8 commit daa514c
Show file tree
Hide file tree
Showing 2 changed files with 116 additions and 42 deletions.
68 changes: 51 additions & 17 deletions tagstudio/src/qt/widgets/button_widgets/base_button_widget.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,11 +8,9 @@
QPainterPath,
QPaintEvent,
QPalette,
QPen,
)
from PySide6.QtWidgets import (
QPushButton,
QWidget,
)
from PySide6.QtWidgets import QPushButton, QWidget

from ... import theme

Expand Down Expand Up @@ -43,20 +41,11 @@ def __init__(self, parent: QWidget | None = None) -> None:
self._repaint_timer.setSingleShot(True)
self._repaint_timer.timeout.connect(self.repaint)

self._init_animations()
self._update_colors() # update colors for the first time

def _init_animations(self) -> None:
"""Initialize animation handlers.
Add properties for background_color, font_color, font_alpha, and corner_radius. and
Initialize animation handlers for changing the background color, font color, corner
radius, and click animation of the widget. Connects valueChanged signals of the animations
to schedule a repaint when values change.
"""
# region Initialize animation handlers.
self.setProperty("background_color", QColor("#00000000"))
self.setProperty("font_color", QColor("#00000000"))
self.setProperty("corner_radius", 10.0)
self.setProperty("focus_anim", 0.0)
self.setProperty("click_anim", 0.0)

background_color_anim = QPropertyAnimation(self, b"background_color", self)
Expand All @@ -74,6 +63,10 @@ def _init_animations(self) -> None:
corner_radius_anim.setEasingCurve(QEasingCurve.Type.OutCubic)
corner_radius_anim.valueChanged.connect(self._schedule_repaint)

focus_anim = QPropertyAnimation(self, b"focus_anim", self)
focus_anim.setDuration(300)
focus_anim.valueChanged.connect(self._schedule_repaint)

click_anim = QPropertyAnimation(self, b"click_anim", self)
click_anim.setDuration(750)
click_anim.setEasingCurve(QEasingCurve.Type.OutCubic)
Expand All @@ -87,8 +80,13 @@ def _init_animations(self) -> None:
"Animation for the font color."
self._corner_radius_anim: QPropertyAnimation = corner_radius_anim
"Animation for the corner radius."
self._focus_anim: QPropertyAnimation = focus_anim
"Animation for the focus indicator."
self._click_anim: QPropertyAnimation = click_anim
"Animation for the button click (the circle animation)."
# endregion

self._update_colors() # update colors for the first time

def _schedule_repaint(self) -> None:
"""Check if the repaint timer is not active and start it with a delay of 0 if so."""
Expand Down Expand Up @@ -178,12 +176,33 @@ def _animate_clicked(self) -> None:
self._click_anim.stop()
self._click_anim.start()

def _set_focus(self, on: bool, animate: bool = True) -> None:
"""Sets the focus indicator show or hide.
Args:
on (bool): Flag indicating whether the focus should be shown.
animate (bool, optional): Flag indicating whether to animate the focus change.
Defaults to True.
"""
self._focus_anim.stop()

value = 1.0 if on else 0.0

if animate:
self._focus_anim.setEndValue(value)
self._focus_anim.start()
else:
self.setProperty("focus_anim", value)
self.repaint()

def focusInEvent(self, arg__1: QFocusEvent) -> None: # noqa: N802
self._update_colors()
self._set_focus(on=True)
return super().focusInEvent(arg__1)

def focusOutEvent(self, arg__1: QFocusEvent) -> None: # noqa: N802
self._update_colors()
self._set_focus(on=False)
return super().focusOutEvent(arg__1)

def enterEvent(self, event: QEnterEvent) -> None: # noqa: N802
Expand Down Expand Up @@ -238,17 +257,20 @@ def paintEvent(self, arg__1: QPaintEvent) -> None: # noqa: N802
font_color: QColor = self.property("font_color")
corner_radius: float = self.property("corner_radius")
click_anim: float = self.property("click_anim")
focus_anim: float = self.property("focus_anim")

button_path = QPainterPath()
button_path.addRoundedRect(self.contentsRect(), corner_radius, corner_radius)

with QPainter(self) as painter:
# painter.setRenderHints(QPainter.RenderHint.Antialiasing, on=True)
painter.setRenderHints(QPainter.RenderHint.Antialiasing, on=True)

# paint background if icon is not set else paint icon
# paint background
painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush(background_color)
painter.drawPath(button_path)

# if icon is not set, paint the icon
if not self.icon().isNull():
self.icon().paint(painter, self.contentsRect(), Qt.AlignmentFlag.AlignCenter)

Expand All @@ -264,6 +286,18 @@ def paintEvent(self, arg__1: QPaintEvent) -> None: # noqa: N802
painter.setPen(font_color)
painter.drawText(self.contentsRect(), Qt.AlignmentFlag.AlignCenter, self.text())

if focus_anim > 0.0:
pen = QPen(
self.palette().color(QPalette.ColorGroup.Active, QPalette.ColorRole.Accent)
)
pen.setWidthF(1.5 * focus_anim)
painter.setPen(pen)
painter.setBrush(Qt.BrushStyle.NoBrush)

painter.drawRoundedRect(
self.contentsRect().adjusted(1, 1, -1, -1), corner_radius, corner_radius
)

def set_corner_radius(self, corner_radius: float, animate: bool = True) -> None:
"""Set the corner radius of the widget.
Expand Down
90 changes: 65 additions & 25 deletions tagstudio/src/qt/widgets/line_edit_widgets/base_line_edit.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,10 @@
QEnterEvent,
QFocusEvent,
QPainter,
QPainterPath,
QPaintEvent,
QPalette,
QPen,
)
from PySide6.QtWidgets import (
QApplication,
Expand Down Expand Up @@ -46,10 +48,27 @@ def drawPrimitive( # noqa: N802
"""
if widget is not None and element == QStyle.PrimitiveElement.PE_PanelLineEdit:
painter.setRenderHint(QPainter.RenderHint.Antialiasing, on=True)

corner_radius: float = widget.property("corner_radius")
focus: float = widget.property("focus_anim")

panel_path = QPainterPath()
panel_path.addRoundedRect(widget.contentsRect(), corner_radius, corner_radius)

painter.setPen(Qt.PenStyle.NoPen)
painter.setBrush(widget.property("background_color"))
corner_radius = widget.property("corner_radius")
painter.drawRoundedRect(widget.rect(), corner_radius, corner_radius)
painter.drawPath(panel_path)

if focus > 0.0:
pen = QPen()
pen.setColor(
widget.palette().color(QPalette.ColorGroup.Active, QPalette.ColorRole.Accent)
)
pen.setWidthF(2 * focus)
painter.setPen(pen)
painter.setClipPath(panel_path)
y = widget.height() - 1
painter.drawLine(0, y, widget.width(), y)
return
return super().drawPrimitive(element, option, painter, widget)

Expand All @@ -58,16 +77,18 @@ class BaseLineEdit(QLineEdit):
"""Initialize a custom Line Edit widget with animated color changes.
This class extends QLineEdit and initializes animation handlers for changing the
background color, font color, and corner radius of the widget. It provides methods
to set corner radius, font alpha, and update colors based on the widget's state.
background color, font color, focus indicator, and corner radius of the widget. It provides
methods to set corner radius, font alpha, and update colors based on the widget's state.
The widget triggers repaints efficiently by using a QTimer.
"""

def __init__(self, parent: QWidget) -> None:
def __init__(self, parent: QWidget | None = None) -> None:
super().__init__(parent=parent)

theme.theme_update_hooks.append(self._update_colors)

self._font_alpha: float = 1.0

self._repaint_timer: QTimer = QTimer()
"""Timer used to schedule repaints. This helps reduce the number of repaints when multiple
animations are running simultaneously."""
Expand All @@ -77,23 +98,13 @@ def __init__(self, parent: QWidget) -> None:
# set defaults
self.setTextMargins(0, 0, 0, 0)
self.setStyle(_ProxyStyle())
self.setFixedHeight(35)

# parts of initialization
self._init_animations()

self._update_colors()

def _init_animations(self) -> None:
"""Initialize animation handlers.
Initialize animation handlers for changing the background color, font color, and corner
radius of the widget. Connects valueChanged signals of the animations to schedule a repaint
when values change.
"""
# region Initialize animation handlers.
self.setProperty("background_color", QColor("#00000000"))
self.setProperty("font_color", QColor("#00000000"))
self.setProperty("font_alpha", 1.0)
self.setProperty("corner_radius", 10.0)
self.setProperty("focus_anim", 0.0)

background_color_anim = QPropertyAnimation(self, b"background_color", self)
background_color_anim.setDuration(500)
Expand All @@ -110,12 +121,21 @@ def _init_animations(self) -> None:
corner_radius_anim.setEasingCurve(QEasingCurve.Type.OutCubic)
corner_radius_anim.valueChanged.connect(self._schedule_repaint)

focus_anim: QPropertyAnimation = QPropertyAnimation(self, b"focus_anim", self)
focus_anim.setDuration(200)
focus_anim.valueChanged.connect(self._schedule_repaint)

self._background_color_anim: QPropertyAnimation = background_color_anim
"Animation for the background color."
self._font_color_anim: QPropertyAnimation = font_color_anim
"Animation for the font color."
self._corner_radius_anim: QPropertyAnimation = corner_radius_anim
"Animation for the corner radius."
self._focus_anim: QPropertyAnimation = focus_anim
"Animation for focus_anim. (Focus indicator)"
# endregion

self._update_colors() # update colors for the first time

def _schedule_repaint(self) -> None:
"""Check if the repaint timer is not active and start it with a delay of 0 if so."""
Expand All @@ -133,8 +153,7 @@ def _update_colors(self, animate: bool = True) -> None:
animate (bool, optional): Flag to indicate whether to animate the color change.
Defaults to True.
"""
# NOTE: using self.palette() for animations. so gettings actual colors from
# QApplication.palette()
# NOTE: using self.palette() for animations. so gettings colors from QApplication.palette()
pal = QApplication.palette()

if not self.isEnabled(): # disabled
Expand All @@ -153,7 +172,7 @@ def _update_colors(self, animate: bool = True) -> None:
bc = pal.color(pal.ColorGroup.Inactive, pal.ColorRole.Button)
fc = pal.color(pal.ColorGroup.Inactive, pal.ColorRole.ButtonText)

fc.setAlphaF(fc.alphaF() * self.property("font_alpha"))
fc.setAlphaF(fc.alphaF() * self._font_alpha)

self._set_colors(
background_color=bc,
Expand Down Expand Up @@ -198,12 +217,33 @@ def _set_colors(
if not animate:
self._schedule_repaint()

def _set_focus(self, on: bool, animate: bool = True) -> None:
"""Sets the focus indicator show or hide.
Args:
on (bool): Flag indicating whether the focus should be shown.
animate (bool, optional): Flag indicating whether to animate the focus change.
Defaults to True.
"""
self._focus_anim.stop()

value = 1.0 if on else 0.0

if animate:
self._focus_anim.setEndValue(value)
self._focus_anim.start()
else:
self.setProperty("focus_anim", value)
self.repaint()

def focusInEvent(self, arg__1: QFocusEvent) -> None: # noqa: N802
self._update_colors()
self._set_focus(on=True)
return super().focusInEvent(arg__1)

def focusOutEvent(self, arg__1: QFocusEvent) -> None: # noqa: N802
self._update_colors()
self._set_focus(on=False)
return super().focusOutEvent(arg__1)

def enterEvent(self, event: QEnterEvent) -> None: # noqa: N802
Expand Down Expand Up @@ -238,15 +278,15 @@ def paintEvent(self, arg__1: QPaintEvent) -> None: # noqa: N802
QPalette.ColorGroup.All, QPalette.ColorRole.Text, self.property("font_color")
)
if QApplication.styleHints().colorScheme() == Qt.ColorScheme.Dark:
placeholder_text_color = self.property("font_color").darker(130)
placeholder_text_color: QColor = self.property("font_color").darker(130)
else:
placeholder_text_color = self.property("font_color").lighter(130)
placeholder_text_color: QColor = self.property("font_color").lighter(130)
palette.setColor(
QPalette.ColorGroup.All,
QPalette.ColorRole.PlaceholderText,
placeholder_text_color,
)
self.setPalette(palette)
super().setPalette(palette) # calling super().setPalette() to avoid recursion
return super().paintEvent(arg__1)

def set_corner_radius(self, corner_radius: float, animate: bool = True) -> None:
Expand Down Expand Up @@ -279,5 +319,5 @@ def set_font_alpha(self, alpha: float, animate: bool = True) -> None:
animate (bool, optional): Flag to indicate whether to animate the font alpha change.
Defaults to True.
"""
self.setProperty("font_alpha", alpha)
self._font_alpha = alpha
self._update_colors(animate)

0 comments on commit daa514c

Please sign in to comment.