Files
Modus/src/shared/window/animated_scrollwindow.py
T
sankalp 54ff85ffc9 fix(services/spotlight): improve MPRIS artwork handling and scope keyword searches to single plugin
- mpris: native MediaArt cache lookup, dedupe play/pause emission, safe
  cleanup, artwork polling for already-playing browser players, drop
  os.system seek fallback
- spotlight: scope plugin keyword and detect() matches to a single plugin
  via search_single instead of global search; register clipboard keywords
  (clip/clipboard)
2026-07-13 22:20:19 +05:45

87 lines
2.7 KiB
Python

from fabric.utils import idle_add
from fabric.widgets.scrolledwindow import ScrolledWindow
from shared.widgets.animator import Animator
class AnimatedScrollable(ScrolledWindow):
def __init__(
self,
bezier_curve: tuple[float, float, float, float] = (0.25, 0.1, 0.25, 1.0),
duration: float = 0.3,
animate: bool = True,
**kwargs,
):
super().__init__(**kwargs)
self._last_req = -1
self.animate = animate
_, min_height = self.min_content_size
_, max_height = self.max_content_size
self.height_animator = Animator(
bezier_curve=bezier_curve,
duration=duration,
min_value=min_height,
max_value=max_height,
)
self.height_animator.connect("notify::value", self.on_animator_change)
self.set_overlay_scrolling(True)
self.connect("notify::child", self._on_child_notified)
if self.get_child():
self._on_child_notified()
def _on_child_notified(self, *_):
child = self.get_child()
if child:
child.connect("size-allocate", self._on_child_size_allocate)
def _on_child_size_allocate(self, widget, allocation):
_, nat_height = widget.get_preferred_height()
target = min(nat_height, self.max_content_size[1])
if target != self._last_req:
self.animate_size(target)
def on_animator_change(self, animator: Animator, *_):
value = round(animator.value)
self.set_min_content_height(value)
self.queue_resize()
def do_animate(
self,
from_height: int = 0,
to_height: int = -1,
):
if to_height == -1:
return
self.height_animator.pause()
self.height_animator.min_value = from_height
self.height_animator.max_value = to_height
self.height_animator.play()
return
def animate_size(self, height: int = -1):
if not self.animate:
idle_add(lambda: self.snap_to_size(height))
return None
self._last_req = height
current_val = self.height_animator.value
return self.do_animate(current_val, height)
def snap_to_size(self, height: int):
"""Instantly resize without animation"""
self._last_req = height
self.height_animator.pause()
self.height_animator.value = height
self.set_min_content_height(height)
self.queue_resize()
def do_get_preferred_height(self):
value = self.height_animator.value
value = 0 if value < 0 else value
return value, value
def destroy(self):
self.height_animator.stop()
super().destroy()