feat(root): implement dynamic todo list refresh and UI enhancements
This change adds a mechanism to refresh the todo list dynamically when the underlying service updates, improving UI responsiveness and user experience. It also updates documentation and removes deprecated files. - New Features: - Added `_refresh_todos` method to rebuild the todo list when `get_todo_service().todos` changes. - Introduced `TodoListCapture` class for mouse capture handling of the todo list window. - Enhancements: - Updated README with corrected image paths, installation tips, and manual installation commands. - Introduced `start.py` to launch the application or lock mode. - Removed deprecated `config.json` and `debug_memory.py`. - Bug Fixes: - Fixed lock screen screenshot path in README.
This commit is contained in:
267 files changed
+24582
-394
No files matched your search
@@ -0,0 +1,263 @@
|
||||
# Author: Yousef EL-Darsh
|
||||
# License (SPDX): AGPL-3.0-or-later
|
||||
|
||||
from functools import cache
|
||||
from typing import Protocol, cast
|
||||
|
||||
from fabric.core.service import Property, Service, Signal
|
||||
from fabric.utils import GLib, Gtk, clamp
|
||||
|
||||
|
||||
@cache
|
||||
def lerp(start: float, end: float, progress: float) -> float:
|
||||
return start + (end - start) * progress
|
||||
|
||||
|
||||
@cache
|
||||
def steps(n: int, progress: float, start_jump: bool = False) -> float:
|
||||
if start_jump:
|
||||
return min(int(progress * n), n - 1) / (n - 1) if n > 1 else 0.0
|
||||
return min(int(progress * n + 1e-10), n) / n
|
||||
|
||||
|
||||
@cache
|
||||
def cubic_bezier(
|
||||
x1: float, y1: float, x2: float, y2: float, progress: float, epsilon=1e-6
|
||||
) -> float:
|
||||
# implementation yanked off of the internet, don't blame me about anything.
|
||||
# Fast-path boundaries to avoid overshoot and unnecessary work
|
||||
if progress <= 0.0 or progress >= 1.0:
|
||||
return clamp(progress, 0.0, 1.0)
|
||||
|
||||
t_guess = progress
|
||||
for _ in range(8):
|
||||
t = t_guess
|
||||
t_sq = t * t
|
||||
omt = 1.0 - t
|
||||
omt_sq = omt * omt
|
||||
|
||||
x = 3 * x1 * omt_sq * t + 3 * x2 * omt * t_sq + t * t_sq
|
||||
dx = 3 * x1 * omt_sq + 6 * (x2 - x1) * omt * t + 3 * (1 - x2) * t_sq
|
||||
|
||||
if abs(dx) < epsilon:
|
||||
break
|
||||
|
||||
delta = (x - progress) / dx
|
||||
t_guess -= delta
|
||||
t_guess = clamp(t_guess, 0.0, 1.0)
|
||||
|
||||
if abs(delta) < epsilon:
|
||||
break
|
||||
|
||||
t = clamp(t_guess, 0.0, 1.0)
|
||||
t_sq = t * t
|
||||
omt = 1.0 - t
|
||||
return 3 * y1 * omt * omt * t + 3 * y2 * omt * t_sq + t * t_sq
|
||||
|
||||
|
||||
def ease_linear(progress: float) -> float:
|
||||
return cubic_bezier(1, 1, 0, 0, progress)
|
||||
|
||||
|
||||
def ease_in(progress: float) -> float:
|
||||
return cubic_bezier(0.4, 0, 1, 1, progress)
|
||||
|
||||
|
||||
def ease_out(progress: float) -> float:
|
||||
return cubic_bezier(0, 0, 0.2, 1, progress)
|
||||
|
||||
|
||||
def ease_in_out(progress: float) -> float:
|
||||
return cubic_bezier(0.4, 0, 0.2, 1, progress)
|
||||
|
||||
|
||||
class TimingFunctionCallback(Protocol):
|
||||
def __call__(self, progress: float, *args, **kwargs) -> float: ...
|
||||
|
||||
|
||||
class Animator(Service):
|
||||
"""
|
||||
An animator is a simple way for animating a value on
|
||||
a set timeline based on a given timing function
|
||||
"""
|
||||
|
||||
@Signal
|
||||
def finished(self) -> None: ...
|
||||
|
||||
@Property(TimingFunctionCallback, "read-write")
|
||||
def timing_function(self) -> TimingFunctionCallback:
|
||||
return self._timing_function
|
||||
|
||||
@timing_function.setter
|
||||
def timing_function(self, value: TimingFunctionCallback):
|
||||
self._timing_function = value
|
||||
return
|
||||
|
||||
@Property(float, "read-write")
|
||||
def duration(self):
|
||||
return self._duration
|
||||
|
||||
@duration.setter
|
||||
def duration(self, value: float):
|
||||
if value <= 0.0:
|
||||
raise ValueError("duration can't be smaller than or equal to 0.0")
|
||||
|
||||
self._duration = value
|
||||
return
|
||||
|
||||
@Property(float, "read-write")
|
||||
def value(self):
|
||||
return self._value
|
||||
|
||||
@value.setter
|
||||
def value(self, value: float):
|
||||
self._value = value
|
||||
return
|
||||
|
||||
@Property(float, "read-write")
|
||||
def max_value(self):
|
||||
return self._max_value
|
||||
|
||||
@max_value.setter
|
||||
def max_value(self, value: float):
|
||||
self._max_value = value
|
||||
return
|
||||
|
||||
@Property(float, "read-write")
|
||||
def min_value(self):
|
||||
return self._min_value
|
||||
|
||||
@min_value.setter
|
||||
def min_value(self, value: float):
|
||||
self._min_value = value
|
||||
return
|
||||
|
||||
@Property(bool, "read-write", default_value=False)
|
||||
def playing(self):
|
||||
return self._playing
|
||||
|
||||
@playing.setter
|
||||
def playing(self, value: bool): # this setter is intended for internal usage only
|
||||
self._playing = value
|
||||
return
|
||||
|
||||
@Property(bool, "read-write", default_value=False)
|
||||
def repeat(self):
|
||||
return self._repeat
|
||||
|
||||
@repeat.setter
|
||||
def repeat(self, value: bool):
|
||||
self._repeat = value
|
||||
return
|
||||
|
||||
def __init__(
|
||||
self,
|
||||
duration: float = 0.8,
|
||||
timing_function: TimingFunctionCallback = ease_linear,
|
||||
value: float = 0.0,
|
||||
min_value: float = 0.0,
|
||||
max_value: float = 1.0,
|
||||
repeat: bool = False,
|
||||
tick_widget: Gtk.Widget | None = None,
|
||||
tick_interval: int = 16,
|
||||
**kwargs,
|
||||
):
|
||||
super().__init__(**kwargs)
|
||||
self._playing = False
|
||||
self._value = value
|
||||
self._min_value = 0.0
|
||||
self._max_value = 1.0
|
||||
self._repeat = False
|
||||
self._duration = 0.8
|
||||
self._timing_function = timing_function
|
||||
self._tick_widget = tick_widget
|
||||
self._tick_interval = tick_interval
|
||||
|
||||
self.timing_function = timing_function
|
||||
self.repeat = repeat
|
||||
self.duration = duration
|
||||
self.min_value = min_value
|
||||
self.max_value = max_value
|
||||
self.value = value
|
||||
self.playing = False
|
||||
|
||||
self._start_time = None
|
||||
self._tick_handler = None
|
||||
self._timeline_pos = 0.0
|
||||
|
||||
def do_get_time_now(self):
|
||||
return GLib.get_monotonic_time() / 1_000_000
|
||||
|
||||
def do_update_value(self, delta_time: float):
|
||||
if not self._playing:
|
||||
return
|
||||
|
||||
elapsed_time = delta_time - cast(float, self._start_time)
|
||||
|
||||
self._timeline_pos = min(1.0, elapsed_time / self._duration)
|
||||
|
||||
self.value = lerp(
|
||||
self._min_value,
|
||||
self._max_value,
|
||||
self._timing_function(progress=self._timeline_pos),
|
||||
)
|
||||
|
||||
if not self._timeline_pos >= 1.0:
|
||||
return
|
||||
|
||||
if not self._repeat:
|
||||
# all done..
|
||||
self.value = self._max_value
|
||||
self.finished()
|
||||
self.pause()
|
||||
return
|
||||
|
||||
self._start_time = delta_time
|
||||
self._timeline_pos = 0.0
|
||||
return
|
||||
|
||||
def do_handle_tick(self, *_):
|
||||
current_time = self.do_get_time_now()
|
||||
self.do_update_value(current_time)
|
||||
return True
|
||||
|
||||
def do_remove_tick_handlers(self):
|
||||
if not self._tick_handler:
|
||||
return
|
||||
|
||||
if self._tick_widget:
|
||||
self._tick_widget.remove_tick_callback(self._tick_handler)
|
||||
else:
|
||||
GLib.source_remove(self._tick_handler)
|
||||
self._tick_handler = None
|
||||
return
|
||||
|
||||
def play(self):
|
||||
if self._playing:
|
||||
return
|
||||
|
||||
self.playing = True
|
||||
self._start_time = self.do_get_time_now()
|
||||
|
||||
if self._tick_handler:
|
||||
return
|
||||
|
||||
if self._tick_widget:
|
||||
self._tick_handler = self._tick_widget.add_tick_callback(
|
||||
self.do_handle_tick
|
||||
)
|
||||
return
|
||||
|
||||
self._tick_handler = GLib.timeout_add(self._tick_interval, self.do_handle_tick)
|
||||
return
|
||||
|
||||
def pause(self):
|
||||
self.playing = False
|
||||
return self.do_remove_tick_handlers()
|
||||
|
||||
def stop(self):
|
||||
if not self._tick_handler:
|
||||
self._timeline_pos = 0
|
||||
self.playing = False
|
||||
return
|
||||
return self.do_remove_tick_handlers()
|
||||
Reference in new issue
Block a user