Skip to content

Commit

Permalink
Add Encyclopaedia.set_entry, emit EncEntry viewed callback when viewe…
Browse files Browse the repository at this point in the history
…d status is changed, not just after Actions
  • Loading branch information
jsfehler committed Nov 10, 2024
1 parent 16aaa4c commit ad7e176
Show file tree
Hide file tree
Showing 6 changed files with 141 additions and 70 deletions.
52 changes: 5 additions & 47 deletions encyclopaedia/actions_ren.py
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
from typing import TYPE_CHECKING

from .constants_ren import SortMode
from .types_ren import ENTRY_TYPE

import renpy.exports as renpy
from renpy.store import DictEquality
from renpy.ui import Action

from typing import TYPE_CHECKING

if TYPE_CHECKING: # pragma: no cover
from .encyclopaedia_ren import Encyclopaedia
from .encentry_ren import EncEntry

from .book import Book

"""renpy
init python:
Expand Down Expand Up @@ -38,54 +36,14 @@ class SetEntry(EncyclopaediaAction):
encyclopaedia: The Encyclopaedia instance to use.
entry: The entry to be made active.
"""
def __init__(self, encyclopaedia: 'Encyclopaedia', entry: 'EncEntry') -> None:
def __init__(self, encyclopaedia: 'Encyclopaedia', entry: ENTRY_TYPE) -> None:
super().__init__(encyclopaedia)

self.entry = entry

def _get_entry_index(self) -> int:
# Find the position of the entry
if self.enc.show_locked_entry:
target_position = self.enc.all_entries.index(self.entry)
else:
target_position = self.enc.unlocked_entries.index(self.entry)

return target_position

def set_entry(self) -> None:
"""Set the Entry as active and update the Encyclopaeda's internal state."""
target_position = self._get_entry_index()

# The active entry is set to whichever list position was found.
self.enc.active = self.entry

if self.enc.active.locked is False:
if not isinstance(self.entry, Book):
if self.entry.viewed is False:
# Run the callback, if provided.
self.entry.emit("viewed")
# Mark the entry as viewed.
self.enc.active.viewed = True

# When setting a Book, set the first page to viewed, not the Book.
elif isinstance(self.entry, Book):
self.entry.active.viewed = True
self.entry.active.emit("viewed")

# When sorting by Unread, setting an entry marks is as read.
# Thus we have to resort the entries to ensure they appear in the
# correct order.
if self.enc.sorting_mode.value == SortMode.UNREAD.value:
self.enc.sort_entries(
entries=self.enc.current_entries,
sorting=self.enc.sorting_mode.value,
)

self.enc.current_position = target_position

def __call__(self) -> None:
"""Used by Ren'Py to invoke this Action."""
self.set_entry()
self.enc.set_entry(self.entry)

# Show the entry screen associated with the encyclopaedia.
renpy.show_screen(self.enc.entry_screen, enc=self.enc)
Expand Down
4 changes: 4 additions & 0 deletions encyclopaedia/encentry_ren.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,8 +182,12 @@ def viewed(self, new_value: bool) -> None:
if self.viewed_persistent:
setattr(persistent, self._name + "_viewed", new_value)

if self._viewed is False and new_value is True:
self.emit("viewed")

self._viewed = new_value


@property
def current_page(self) -> 'EncEntry':
"""Get the sub-page that's currently viewing viewed.
Expand Down
101 changes: 78 additions & 23 deletions encyclopaedia/encyclopaedia_ren.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,10 @@
ToggleShowLockedEntryAction,
)
from .entry_sorting_ren import push_locked_to_bottom
from .exceptions_ren import AddEntryError
from .exceptions_ren import AddEntryError, UnknownEntryError
from .eventemitter_ren import EventEmitter
from .constants_ren import Direction, SortMode
from .book import Book

from .types_ren import ENTRY_TYPE

if TYPE_CHECKING: # pragma: no cover
Expand Down Expand Up @@ -115,12 +114,17 @@ def __str__(self) -> str: # NOQA D105

def __len__(self) -> int:
"""The total number of entries, relative to if locked ones are shown or not."""
rv = len(self.unlocked_entries)
if self.show_locked_entry:
rv = len(self.all_entries)

rv = len(self.viewable_entries)
return rv

@property
def viewable_entries(self):
"""Get the list of entries which are currently viewable."""
if self.show_locked_entry:
return self.all_entries
else:
return self.unlocked_entries

@property
def current_entries(self) -> list[ENTRY_TYPE]:
"""Get all the entries which should be visible to the user.
Expand Down Expand Up @@ -148,10 +152,7 @@ def current_entry(self) -> ENTRY_TYPE:
Return:
EncEntry
"""
entry = self.unlocked_entries[self.current_position]
if self.show_locked_entry:
entry = self.all_entries[self.current_position]

entry = self.viewable_entries[self.current_position]
return entry

@property
Expand Down Expand Up @@ -311,8 +312,73 @@ def _build_subject_filter(self, subject: str) -> None:

self.filtered_entries = [i for i in entries if i.subject == subject]

def _change_active_entry_viewed_status(self) -> bool:
"""Change the viewed status of the active Entry.
Return:
True if status was changed, else False
"""
if self.active is None:
raise ValueError(
'Tried to change active entry viewed status with no active entry.',
)
if self.active.locked is False:
entry = None

if isinstance(self.active, Book):
# When Book, set the first page to viewed, not the Book.
entry = self.active.active
else:
entry = self.active

# Mark the entry as viewed.
entry.viewed = True

return True

return False

def set_entry(self, entry: ENTRY_TYPE) -> None:
"""Set an Entry as active.
Args:
entry: The Entry to set.
Raises:
ValueError: If the entry cannot be set.
"""
try:
target_position = self.viewable_entries.index(entry)
except ValueError as e:
if entry not in self.all_entries:
raise UnknownEntryError(f"{entry} is not in this Encyclopaedia") from e
else:
raise ValueError(
f"{entry} cannot be set because it is locked and 'show_locked_entry' is False",
) from e

self.current_position = target_position

self.active = entry
self._change_active_entry_viewed_status()

# When sorting by Unread, setting an entry marks is as read.
# Thus we have to resort the entries to ensure they appear in the
# correct order.
if self.sorting_mode.value == SortMode.UNREAD.value:
self.sort_entries(
entries=self.current_entries,
sorting=self.sorting_mode.value,
)

def _change_entry(self, direction: Direction) -> bool:
"""Change the current active EncEntry."""
"""Change the active entry by changing the index.
This is relative to the current sorting.
Args:
direction: The direction to move.
"""
test_position = self.current_position + direction.value

# Boundary check
Expand All @@ -328,18 +394,7 @@ def _change_entry(self, direction: Direction) -> bool:
# Update the active entry.
self.active = self.current_entry

if self.active.locked is False:
if not isinstance(self.active, Book):
# Run the callback, if provided.
self.active.emit("viewed")

# Mark the entry as viewed.
self.active.viewed = True

# When setting a Book, set the first page to viewed, not the Book.
elif isinstance(self.active, Book):
self.active.active.viewed = True
self.active.active.emit("viewed")
self._change_active_entry_viewed_status()

# When changing an entry, the current entry page number is reset.
self.active._unlocked_page_index = 0
Expand Down
6 changes: 6 additions & 0 deletions encyclopaedia/exceptions_ren.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,9 @@ class AddEntryError(Exception):

class GetEntryError(Exception):
"""Raised when getting an entry fails."""
pass


class UnknownEntryError(Exception):
"""Raised when looking for an entry that is not in an Encyclopaedia."""
pass
File renamed without changes.
48 changes: 48 additions & 0 deletions tests/encyclopaedia/test_set_entry.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
from encyclopaedia import Encyclopaedia
from encyclopaedia import EncEntry
from encyclopaedia.exceptions_ren import UnknownEntryError

import pytest


def test__change_active_entry_viewed_status_no_active():
enc = Encyclopaedia()

EncEntry(
parent=enc,
name="Test Name",
text=["Test Text"],
)

with pytest.raises(ValueError):
enc._change_active_entry_viewed_status()


def test__change_active_entry_viewed_status_locked_entry():
enc = Encyclopaedia()

e = EncEntry(
parent=enc,
name="Test Name",
text=["Test Text"],
locked=True,
)

enc.active = e

result = enc._change_active_entry_viewed_status()
assert result is False


def test_set_entry_unknown_entry():
enc = Encyclopaedia()
another_enc = Encyclopaedia()

e = EncEntry(
parent=enc,
name="Test Name",
text=["Test Text"],
)

with pytest.raises(UnknownEntryError):
another_enc.set_entry(e)

0 comments on commit ad7e176

Please sign in to comment.