Skip to content

Commit

Permalink
Move the generation count out of NotificationSource.
Browse files Browse the repository at this point in the history
This is used in only one place, and does not really fit with the argument-passing fire() method we have now, so I'll move it.
  • Loading branch information
danieljohnson2 committed May 12, 2024
1 parent 189c02b commit ce49b92
Show file tree
Hide file tree
Showing 3 changed files with 26 additions and 15 deletions.
2 changes: 1 addition & 1 deletion lutris/game.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
from gettext import gettext as _
from typing import cast

from gi.repository import Gio, GLib, GObject, Gtk
from gi.repository import Gio, GLib, Gtk

from lutris import settings
from lutris.config import LutrisConfig
Expand Down
22 changes: 10 additions & 12 deletions lutris/gui/widgets/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,15 @@ def unregister(self) -> None:


class NotificationSource:
"""A class to inform interested code of changes in a global, like a signal but not attached to any
object."""
"""A class to inform interested code of changes or of an event; these are often global objects,
but occasionally are attached to another object to avoid having to unregister handlers as much.
The fire() method may be passed arguments, and these are passed on to any handlers; often there's
a single argument which is the object (like a Game) that has experienced some event or change. This
handler can't return anything, because it is called at idle time, not immediately.
"""

def __init__(self) -> None:
self._generation_number = 0
self._callbacks: Dict[int, Callable] = {}
self._next_callback_id = 1
self._scheduled_callbacks: List[Tuple[Callable, Tuple, Dict]] = []
Expand All @@ -46,19 +50,13 @@ def has_handlers(self) -> bool:
return bool(self._callbacks)

def fire(self, *args, **kwargs) -> None:
"""Signals that the thing, whatever it is, has happened. This increments the generation number,
and schedules the callbacks to run (if they are not scheduled already)."""
self._generation_number += 1
"""Signals that the thing, whatever it is, has happened. This does not invoke the callbacks
at once, but schedules the callbacks to run at idle time. This ensures handlers always run
on the UI thread, for safety."""
for callback in self._callbacks.values():
self._scheduled_callbacks.append((callback, args, kwargs))
schedule_at_idle(self._notify)

@property
def generation_number(self) -> int:
"""Returns a number that is incremented on each call to fire(). This can be polled
passively, when registering a callback is inappropriate."""
return self._generation_number

def register(self, callback: Callable) -> NotificationRegistration:
"""Registers a callback to be called after the thing, whatever it is, has happened;
fire() schedules callbacks to be called at idle time on the main thread.
Expand Down
17 changes: 15 additions & 2 deletions lutris/gui/widgets/cellrenderers.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@
from lutris.services.service_media import resolve_media_path
from lutris.util.path_cache import MISSING_GAMES

_MEDIA_CACHE_GENERATION_NUMBER = 0


class GridViewCellRendererText(Gtk.CellRendererText):
"""CellRendererText adjusted for grid view display, removes extra padding
Expand Down Expand Up @@ -509,8 +511,8 @@ def _get_cached_surface_by_path(self, widget, path, size=None, preserve_aspect_r
in this render, but we'll clear that cache when the media generation number is changed,
or certain properties are. We also age surfaces from the cache at idle time after
rendering."""
if self.cached_surface_generation != MEDIA_CACHE_INVALIDATED.generation_number:
self.cached_surface_generation = MEDIA_CACHE_INVALIDATED.generation_number
if self.cached_surface_generation != _MEDIA_CACHE_GENERATION_NUMBER:
self.cached_surface_generation = _MEDIA_CACHE_GENERATION_NUMBER
self.clear_cache()

key = widget, path, size, preserve_aspect_ratio
Expand All @@ -534,3 +536,14 @@ def _get_surface_by_path(self, widget, path, size=None, preserve_aspect_ratio=Tr
cell_size = size or (self.media_width, self.media_height)
scale_factor = widget.get_scale_factor() if widget else 1
return get_scaled_surface_by_path(path, cell_size, scale_factor, preserve_aspect_ratio=preserve_aspect_ratio)


def _on_media_cached_invalidated() -> None:
# Increment a counter, so we can passively detect when the media cache is invalid
# without a per-object handler. We have no way to unregister that handler, but we never
# need to unregister this global one.
global _MEDIA_CACHE_GENERATION_NUMBER
_MEDIA_CACHE_GENERATION_NUMBER += 1


MEDIA_CACHE_INVALIDATED.register(_on_media_cached_invalidated)

0 comments on commit ce49b92

Please sign in to comment.