Skip to content

Commit

Permalink
feat(animation): add animation settings for status bar
Browse files Browse the repository at this point in the history
This commit introduces new configuration options for animating the status bar. The `animation` object can now be specified in the configuration, allowing users to enable or disable animations and set their duration. The default values are set to enable animations with a duration of 500 milliseconds.
  • Loading branch information
amnweb committed Dec 3, 2024
1 parent d41a103 commit a171479
Show file tree
Hide file tree
Showing 3 changed files with 90 additions and 54 deletions.
5 changes: 3 additions & 2 deletions docs/Configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ All valid options for the widgets are listed on the widgets page.
| `window_flags` | object | `{always_on_top: false, windows_app_bar: true}` | The window flags for the status bar. |
| `dimensions` | object | `{width: "100%", height: 36}` | The dimensions of the status bar. |
| `padding` | object | `{top: 4, left: 0, bottom: 4, right: 0}` | The padding for the status bar. |
| `animation` | object | `{enabled: true, duration: 500}` | The animation settings for the status bar. Duration is in milliseconds. |
| `widgets` | list | `left[],center[],right[]` | Active widgets and position. |

# Multiple Bars Example
Expand Down Expand Up @@ -68,7 +69,7 @@ widgets:
We used Windows API for blur, and because of this some parts are limited with the OS.

`blur_effect.enabled` Will enable defaul blur.<br>
`blur_effect.acrylic` Enable an acrylic blur effect behind a window.<br>
`blur_effect.acrylic` Enable an acrylic blur effect behind a window. (Windows 10)<br>
`blur_effect.dark_mode` Dark mode and more shadow below bar.<br>
`blur_effect.round_corners` True or False, if set to True Windows will add radius. You can't set a custom value.<br>
`blur_effect.border_color` Border color for bar can be `None`, `System` or `Hex Color`. (This applies to system round_corners and if blur_effect.round_corners is True.)
`blur_effect.border_color` Border color for bar can be `None`, `System` or `Hex Color` `"#ff0000"`. (This applies to system round_corners and if blur_effect.round_corners is True.)
122 changes: 70 additions & 52 deletions src/core/bar.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
import logging
from settings import APP_BAR_TITLE
from PyQt6.QtWidgets import QApplication, QWidget, QHBoxLayout, QGridLayout, QFrame
from PyQt6.QtWidgets import QWidget, QHBoxLayout, QGridLayout, QFrame
from PyQt6.QtGui import QScreen
from PyQt6.QtCore import Qt, QRect, QEvent
from PyQt6.QtCore import Qt, QRect, QEvent, QPropertyAnimation, QEasingCurve, QPoint
from core.utils.utilities import is_valid_percentage_str, percent_to_float
from core.utils.win32.utilities import get_monitor_hwnd
from core.validation.bar import BAR_DEFAULTS
Expand All @@ -26,6 +26,7 @@ def __init__(
class_name: str = BAR_DEFAULTS['class_name'],
alignment: dict = BAR_DEFAULTS['alignment'],
blur_effect: dict = BAR_DEFAULTS['blur_effect'],
animation: dict = BAR_DEFAULTS['animation'],
window_flags: dict = BAR_DEFAULTS['window_flags'],
dimensions: dict = BAR_DEFAULTS['dimensions'],
padding: dict = BAR_DEFAULTS['padding']
Expand All @@ -39,12 +40,15 @@ def __init__(
self._window_flags = window_flags
self._dimensions = dimensions
self._padding = padding
self._animation = animation
self._is_dark_theme = None

self.screen_name = self.screen().name()
self.app_bar_edge = app_bar.AppBarEdge.Top \
if self._alignment['position'] == "top" \
self.app_bar_edge = (
app_bar.AppBarEdge.Top
if self._alignment['position'] == "top"
else app_bar.AppBarEdge.Bottom
)

if self._window_flags['windows_app_bar'] and IMPORT_APP_BAR_MANAGER_SUCCESSFUL:
self.app_bar_manager = app_bar.Win32AppBar()
Expand Down Expand Up @@ -79,40 +83,12 @@ def __init__(
)

self.screen().geometryChanged.connect(self.on_geometry_changed, Qt.ConnectionType.QueuedConnection)
self.show()


def detect_os_theme(self) -> bool:
try:
import winreg
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as registry:
with winreg.OpenKey(registry, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize') as key:
value, _ = winreg.QueryValueEx(key, 'AppsUseLightTheme')
return value == 0
except Exception as e:
logging.error(f"Failed to determine Windows theme: {e}")
return False
if self._animation['enabled']:
self.animation_bar()
self.show()


def update_theme_class(self):
is_dark_theme = self.detect_os_theme()
# Possible there is better solution for this, but in this way we can prevent MS events spam
if is_dark_theme != self._is_dark_theme:
class_property = self._bar_frame.property("class")
if is_dark_theme:
class_property += " dark"
else:
class_property = class_property.replace(" dark", "")
self._bar_frame.setProperty("class", class_property)
update_styles(self._bar_frame)
self._is_dark_theme = is_dark_theme


def event(self, event: QEvent) -> bool:
# Update theme class when system theme changes
if event.type() == QEvent.Type.ApplicationPaletteChange:
self.update_theme_class()
return super().event(event)



@property
def bar_id(self) -> str:
Expand Down Expand Up @@ -150,22 +126,8 @@ def position_bar(self, init=False) -> None:
bar_width = self._dimensions['width']
bar_height = self._dimensions['height']

#screen_scale = self.screen().devicePixelRatio()
screen_width = self.screen().geometry().width()
screen_height = self.screen().geometry().height()

# Commented out for now, as it causes issues with the bar position on 4k monitors, probably we don't need it anymore.
# Fix for non-primary display Windows OS scaling on app startup
# should_downscale_screen_geometry = (
# init and
# len(QApplication.screens()) > 1 and
# screen_scale >= 2.0 and
# QApplication.primaryScreen() != self.screen()
# )

# if should_downscale_screen_geometry:
# screen_width = screen_width / screen_scale
# screen_height = screen_height / screen_scale

if is_valid_percentage_str(str(self._dimensions['width'])):
bar_width = int(screen_width * percent_to_float(self._dimensions['width']) - self._padding['left'] - self._padding['right'])
Expand All @@ -185,7 +147,6 @@ def _add_widgets(self, widgets: dict[str, list] = None):
bar_layout = QGridLayout()
bar_layout.setContentsMargins(0, 0, 0, 0)
bar_layout.setSpacing(0)


for column_num, layout_type in enumerate(['left', 'center', 'right']):
layout = QHBoxLayout()
Expand All @@ -212,6 +173,63 @@ def _add_widgets(self, widgets: dict[str, list] = None):

self._bar_frame.setLayout(bar_layout)


def animation_bar(self):
self.final_pos = self.pos()
if self._alignment['position'] == "top":
self.initial_pos = QPoint(self.final_pos.x(), self.final_pos.y() - self.height())
else:
screen_height = self.screen().geometry().height()
self.initial_pos = QPoint(self.final_pos.x(), self.final_pos.y() + screen_height)
self.move(self.initial_pos)
self.animation = QPropertyAnimation(self, b"pos")
self.animation.setDuration(self._animation['duration'])
self.animation.setStartValue(self.initial_pos)
self.animation.setEndValue(self.final_pos)
self.animation.setEasingCurve(QEasingCurve.Type.OutExpo)


def showEvent(self, event):
super().showEvent(event)
try:
self.animation.start()
except AttributeError:
logging.error("Animation not initialized.")


def detect_os_theme(self) -> bool:
try:
import winreg
with winreg.ConnectRegistry(None, winreg.HKEY_CURRENT_USER) as registry:
with winreg.OpenKey(registry, r'SOFTWARE\Microsoft\Windows\CurrentVersion\Themes\Personalize') as key:
value, _ = winreg.QueryValueEx(key, 'AppsUseLightTheme')
return value == 0
except Exception as e:
logging.error(f"Failed to determine Windows theme: {e}")
return False


def update_theme_class(self):
if not hasattr(self, '_bar_frame'):
return

is_dark_theme = self.detect_os_theme()
if is_dark_theme != self._is_dark_theme:
class_property = self._bar_frame.property("class")
if is_dark_theme:
class_property += " dark"
else:
class_property = class_property.replace(" dark", "")
self._bar_frame.setProperty("class", class_property)
update_styles(self._bar_frame)
self._is_dark_theme = is_dark_theme


def changeEvent(self, event: QEvent) -> None:
if event.type() == QEvent.Type.PaletteChange:
self.update_theme_class()
super().changeEvent(event)

def update_styles(widget):
widget.style().unpolish(widget)
widget.style().polish(widget)
Expand Down
17 changes: 17 additions & 0 deletions src/core/validation/bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
'class_name': 'yasb-bar',
'alignment': {'position': 'top', 'center': False},
'blur_effect': {'enabled': False, 'dark_mode': False, 'acrylic': False,'round_corners': False,'border_color': "System"},
'animation': {'enabled': True, 'duration': 500},
'window_flags': {'always_on_top': False, 'windows_app_bar': False},
'dimensions': {'width': '100%', 'height': 30},
'padding': {'top': 0, 'left': 0, 'bottom': 0, 'right': 0},
Expand Down Expand Up @@ -71,6 +72,22 @@
},
'default': BAR_DEFAULTS['blur_effect']
},
'animation': {
'type': 'dict',
'required': False,
'schema': {
'enabled': {
'type': 'boolean',
'default': BAR_DEFAULTS['animation']['enabled']
},
'duration': {
'type': 'integer',
'min': 0,
'default': BAR_DEFAULTS['animation']['duration']
}
},
'default': BAR_DEFAULTS['animation']
},
'window_flags': {
'type': 'dict',
'schema': {
Expand Down

0 comments on commit a171479

Please sign in to comment.