Skip to content

Commit

Permalink
Merge pull request #179 from Yutsuten/slow-full-recover
Browse files Browse the repository at this point in the history
Allow to full recover slowly
  • Loading branch information
Yutsuten authored Apr 26, 2024
2 parents 378fa01 + b88774a commit 56d01b8
Show file tree
Hide file tree
Showing 7 changed files with 80 additions and 54 deletions.
2 changes: 1 addition & 1 deletion src/database.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ def update(self, new_conf: dict[str, Any]) -> None:
class DeckConf:
"""Manages Life Drain's deck configuration."""
FIELDS: ClassVar[set[str]] = {
'enable', 'maxLife', 'recover', 'damage', 'damageNew', 'damageLearning',
'enable', 'maxLife', 'recover', 'damage', 'damageNew', 'damageLearning', 'fullRecoverSpeed',
}

def __init__(self, mw: AnkiQt):
Expand Down
21 changes: 18 additions & 3 deletions src/deck_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
from typing import TYPE_CHECKING, Any, Literal, Optional, Union

from anki.hooks import runHook
from aqt.progress import ProgressManager

from .decorators import must_have_active_deck
from .defaults import BEHAVIORS
Expand Down Expand Up @@ -34,6 +35,9 @@ def __init__(self, mw: AnkiQt, qt: Any, global_conf: GlobalConf, deck_conf: Deck
global_conf: An instance of GlobalConf.
deck_conf: An instance of DeckConf.
"""
self.recovering: bool = False
self.timer = ProgressManager(mw).timer(100, self.life_timer, repeat=True, parent=mw)
self.timer.stop()
self._progress_bar = ProgressBar(mw, qt)
self._global_conf = global_conf
self._deck_conf = deck_conf
Expand Down Expand Up @@ -82,6 +86,7 @@ def set_deck_conf(self, conf: dict[str, Any], *, update_life: bool) -> None:
bar_info['enable'] = conf['enable']
bar_info['maxValue'] = conf['maxLife']
bar_info['recoverValue'] = conf['recover']
bar_info['fullRecoverSpeed'] = conf['fullRecoverSpeed']
bar_info['damageValue'] = conf['damage']
bar_info['damageNew'] = conf['damageNew']
bar_info['damageLearning'] = conf['damageLearning']
Expand All @@ -93,9 +98,18 @@ def set_deck_conf(self, conf: dict[str, Any], *, update_life: bool) -> None:
bar_info['currentValue'] = current_value

@must_have_active_deck
def drain(self, bar_info: dict[str, Any]) -> None:
"""Life loss due to drain."""
self._update_life(bar_info, -0.1)
def life_timer(self, bar_info: dict[str, Any]) -> None:
"""Life loss due to drain, or life gained due to recover."""
if self.recovering:
if bar_info['fullRecoverSpeed'] == 0:
self.recover()
else:
self._update_life(bar_info, bar_info['fullRecoverSpeed'] / 10)
else:
self._update_life(bar_info, -0.1) # Drain

if bar_info['currentValue'] in [0, bar_info['maxValue']]:
self.timer.stop()

@must_have_active_deck
def heal(self, bar_info: dict[str, Any], value:Optional[Union[int, float]]=None, *,
Expand Down Expand Up @@ -231,6 +245,7 @@ def _add_deck(self, deck_id:str) -> None:
'enable': conf['enable'],
'maxValue': conf['maxLife'],
'recoverValue': conf['recover'],
'fullRecoverSpeed': conf['fullRecoverSpeed'],
'damageValue': conf['damage'],
'damageNew': conf['damageNew'],
'damageLearning': conf['damageLearning'],
Expand Down
1 change: 1 addition & 0 deletions src/defaults.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
DEFAULTS = {
'maxLife': 120,
'recover': 5,
'fullRecoverSpeed': 0,
'damage': None,
'damageNew': None,
'damageLearning': None,
Expand Down
53 changes: 27 additions & 26 deletions src/lifedrain.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Callable, Union
from typing import TYPE_CHECKING, Any, Union

from . import settings
from .database import DeckConf, GlobalConf
Expand All @@ -27,18 +27,15 @@ class Lifedrain:
status: A dictionary that keeps track the events on Anki.
"""

def __init__(self, make_timer: Callable, mw: AnkiQt, qt: Any):
def __init__(self, mw: AnkiQt, qt: Any):
"""Initializes DeckManager and Settings, and add-on initial setup.
Args:
make_timer: A function that creates a timer.
mw: Anki's main window.
qt: The PyQt library.
"""
self._qt = qt
self._mw = mw
self._timer = make_timer(100, lambda: self.deck_manager.drain(), repeat=True, parent=mw)
self._timer.stop()
self.config = GlobalConf(mw)
self._deck_config = DeckConf(mw)
self.deck_manager = DeckManager(mw, qt, self.config, self._deck_config)
Expand All @@ -53,8 +50,8 @@ def __init__(self, make_timer: Callable, mw: AnkiQt, qt: Any):

def global_settings(self) -> None:
"""Opens a dialog with the Global Settings."""
drain_enabled = self._timer.isActive()
self._toggle_drain(enable=False)
drain_enabled = self.deck_manager.timer.isActive()
self.toggle_drain(enable=False)
settings.global_settings(
aqt=self._qt,
mw=self._mw,
Expand All @@ -64,24 +61,24 @@ def global_settings(self) -> None:
config = self.config.get()
if config['enable']:
self.update_global_shortcuts()
self._toggle_drain(drain_enabled)
self.toggle_drain(drain_enabled)
self.deck_manager.update(self.status['screen'])
else:
self.update_global_shortcuts()
self.deck_manager.hide_life_bar()

def deck_settings(self) -> None:
"""Opens a dialog with the Deck Settings."""
drain_enabled = self._timer.isActive()
self._toggle_drain(enable=False)
drain_enabled = self.deck_manager.timer.isActive()
self.toggle_drain(enable=False)
settings.deck_settings(
aqt=self._qt,
mw=self._mw,
config=self._deck_config,
global_config=self.config,
deck_manager=self.deck_manager,
)
self._toggle_drain(drain_enabled)
self.toggle_drain(drain_enabled)
self.deck_manager.update(self.status['screen'])

def update_global_shortcuts(self) -> None:
Expand All @@ -102,17 +99,18 @@ def review_shortcuts(self, shortcuts: list[tuple]) -> None:
if config['deckSettingsShortcut']:
shortcuts.append((config['deckSettingsShortcut'], self.deck_settings))
if config['enable'] and config['pauseShortcut']:
shortcuts.append((config['pauseShortcut'], self._toggle_drain))
shortcuts.append((config['pauseShortcut'], self.toggle_drain))

def overview_shortcuts(self, shortcuts: list[tuple]) -> None:
"""Generates the overview screen shortcuts."""
config = self.config.get()
if config['deckSettingsShortcut']:
shortcuts.append((config['deckSettingsShortcut'], self.deck_settings))
if config['enable'] and config['recoverShortcut']:
def full_recover() -> None:
self.deck_manager.recover()
shortcuts.append((config['recoverShortcut'], full_recover))
def start_recover() -> None:
self.deck_manager.recovering = True
self.toggle_drain()
shortcuts.append((config['recoverShortcut'], start_recover))

def screen_change(self, state: MainWindowState) -> None:
"""Updates Life Drain when the screen changes.
Expand All @@ -130,25 +128,27 @@ def screen_change(self, state: MainWindowState) -> None:

self.deck_manager.update(state)
if state != 'review':
self._toggle_drain(enable=False)
self.toggle_drain(enable=False)
self.status['prev_card'] = None
if self.status['reviewed'] and state in ['overview', 'review']:
if state != 'overview':
self.deck_manager.recovering = False
if state != 'deckBrowser' and self.status['reviewed']:
self.deck_manager.answer(
self.status['review_response'],
self.status['card_type'],
)
self.status['reviewed'] = False
self.status['reviewed'] = False

@must_be_enabled
def opened_window(self, config: dict[str, Any]) -> None:
"""Called when a window is opened while reviewing."""
if config['stopOnLostFocus']:
self._toggle_drain(enable=False)
self.toggle_drain(enable=False)

@must_be_enabled
def show_question(self, config: dict[str, Any], card: Card) -> None:
"""Called when a question is shown."""
self._toggle_drain(enable=True)
self.toggle_drain(enable=True)
if self.status['action'] == 'undo':
self.deck_manager.undo()
elif self.status['action'] == 'bury':
Expand All @@ -169,18 +169,19 @@ def show_question(self, config: dict[str, Any], card: Card) -> None:
@must_be_enabled
def show_answer(self, config: dict[str, Any]) -> None:
"""Called when an answer is shown."""
self._toggle_drain(not config['stopOnAnswer'])
self.toggle_drain(not config['stopOnAnswer'])
self.status['reviewed'] = True

@must_be_enabled
def _toggle_drain(self, config: dict[str, Any], enable: Union[bool, None]=None) -> None: # noqa: ARG002
def toggle_drain(self, config: dict[str, Any], enable: Union[bool, None]=None) -> None: # noqa: ARG002
"""Toggles the life drain.
Args:
config: Global configuration dictionary.
enable: Optional. Enables the drain if True.
"""
if self._timer.isActive() and enable is not True:
self._timer.stop()
elif not self._timer.isActive() and enable is not False:
self._timer.start()
is_active = self.deck_manager.timer.isActive()
if is_active and enable is not True:
self.deck_manager.timer.stop()
elif not is_active and enable is not False:
self.deck_manager.timer.start()
7 changes: 3 additions & 4 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
from anki import hooks
from anki.decks import DeckId
from aqt import gui_hooks, mw, qt
from aqt.progress import ProgressManager

from .defaults import DEFAULTS
from .exceptions import GetCollectionError, GetMainWindowError
Expand All @@ -20,8 +19,7 @@ def main() -> None:
if mw is None:
raise GetMainWindowError

make_timer = ProgressManager(mw).timer
lifedrain = Lifedrain(make_timer, mw, qt)
lifedrain = Lifedrain(mw, qt)

setup_shortcuts(lifedrain)
setup_state_change(lifedrain)
Expand Down Expand Up @@ -82,7 +80,8 @@ def custom_link_handler(url: str) -> bool:
if url == 'lifedrain':
lifedrain.deck_settings()
elif url == 'recover':
lifedrain.deck_manager.recover()
lifedrain.deck_manager.recovering = True
lifedrain.toggle_drain()
return link_handler(url=url)

return custom_link_handler
Expand Down
34 changes: 15 additions & 19 deletions src/progress_bar.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,8 @@

from __future__ import annotations

from typing import TYPE_CHECKING, Any, Literal, Union
import math
from typing import TYPE_CHECKING, Any, Literal

from .defaults import POSITION_OPTIONS, STYLE_OPTIONS, TEXT_FORMAT

Expand All @@ -28,7 +29,7 @@ def __init__(self, mw: AnkiQt, qt: Any):
self._mw = mw
self._qt = qt
self._qprogressbar = qt.QProgressBar()
self._current_value: Union[int, float] = 1
self._current_value: float = 1
self._dock: dict[str, Any] = {}
self._max_value: float = 1
self._text_format: str = ''
Expand Down Expand Up @@ -56,18 +57,16 @@ def set_max_value(self, max_value: float) -> None:
Args:
max_value: The maximum value of the bar. Up to 1 decimal place.
"""
self._max_value = max_value * 10
if self._max_value <= 0:
self._max_value = 1
self._qprogressbar.setRange(0, self._max_value)
self._max_value = max(1, max_value)
self._qprogressbar.setRange(0, self._max_value * 10)

def set_current_value(self, current_value: float) -> None:
"""Sets the current value for the bar.
Args:
current_value: The current value of the bar. Up to 1 decimal place.
"""
self._current_value = int(current_value * 10)
self._current_value = current_value
self._validate_current_value()
self._update_text()
self._update_bar_color()
Expand All @@ -78,15 +77,14 @@ def inc_current_value(self, increment: float) -> None:
Args:
increment: A positive or negative number. Up to 1 decimal place.
"""
self._current_value += int(increment * 10)
self._current_value += increment
self._validate_current_value()
if self._current_value % 10 == 0 or abs(increment) >= 1:
self._update_text()
self._update_bar_color()
self._update_text()
self._update_bar_color()

def get_current_value(self) -> float:
"""Gets the current value of the bar."""
return float(self._current_value) / 10
return self._current_value

def set_style(self, options: dict[str, Any]) -> None:
"""Sets the styling of the Progress Bar.
Expand Down Expand Up @@ -152,22 +150,20 @@ def _validate_current_value(self) -> None:
self._current_value = self._max_value
elif self._current_value < 0:
self._current_value = 0
self._qprogressbar.setValue(self._current_value)
self._qprogressbar.setValue(int(self._current_value * 10))
self._qprogressbar.update()

def _update_text(self) -> None:
"""Updates the Progress Bar text."""
if not self._text_format:
return
if self._text_format == 'mm:ss':
minutes = int(self._current_value / 600)
seconds = int((self._current_value / 10) % 60)
minutes = int(self._current_value / 60)
seconds = int(self._current_value) % 60
self._qprogressbar.setFormat(f'{minutes:01d}:{seconds:02d}')
else:
current_value = int(self._current_value / 10)
if self._current_value % 10 != 0:
current_value += 1
max_value = int(self._max_value / 10)
current_value = math.ceil(self._current_value)
max_value = int(self._max_value)
text = self._text_format
text = text.replace('%v', str(current_value))
text = text.replace('%m', str(max_value))
Expand Down
Loading

0 comments on commit 56d01b8

Please sign in to comment.