def test_change(shortcut_engine, mocker): callback = mocker.Mock(return_value=True) old_shortcut = Shortcut("start", "<control>a") new_shortcut = Shortcut("start", "<control>b") shortcut_engine.connect(old_shortcut, callback) shortcut_engine.change(new_shortcut) assert active_shortcut(shortcut_engine, old_shortcut) is False assert active_shortcut(shortcut_engine, new_shortcut) is True key, mod = Gtk.accelerator_parse(new_shortcut.value) callback.assert_called_once_with(shortcut_engine.accel_group, mocker.ANY, key, mod)
def test_disconnect(shortcut_engine, mocker): shortcut = Shortcut("start", "<control>s") shortcut_engine.connect(shortcut, mocker.Mock()) shortcut_engine.disconnect(shortcut) assert active_shortcut(shortcut_engine, shortcut) is False
def test_connect(shortcut_engine, mocker): callback = mocker.Mock(return_value=True) shortcut = Shortcut("start", "<control>s") shortcut_engine.connect(shortcut, callback) assert active_shortcut(shortcut_engine, shortcut) is True key, mod = Gtk.accelerator_parse(shortcut.value) callback.assert_called_once_with(shortcut_engine.accel_group, mocker.ANY, key, mod)
class Menu(Subscriber): PREFERENCE_SHORTCUT = Shortcut("session.settings", "<control>comma") @inject( bus="tomate.bus", about="tomate.ui.about", preference="tomate.ui.preference", shortcuts="tomate.ui.shortcut", ) def __init__(self, bus: Bus, about, preference, shortcuts: ShortcutEngine): self.connect(bus) self.widget = Gtk.Menu(halign=Gtk.Align.CENTER) self.widget.add(self._create_menu_item("header.menu.preference", _("Preferences"), preference.widget)) self.widget.add(self._create_menu_item("header.menu.about", _("About"), about.widget)) self.widget.show_all() shortcuts.connect(Menu.PREFERENCE_SHORTCUT, lambda *_: preference.widget.run()) def _create_menu_item(self, name: str, label: str, dialog: Gtk.Dialog) -> Gtk.MenuItem: menu_item = Gtk.MenuItem.new_with_label(label) menu_item.props.name = name menu_item.connect("activate", lambda *_: dialog.run()) return menu_item
class SessionButton(Subscriber): POMODORO_SHORTCUT = Shortcut("session.pomodoro", "<control>1") SHORT_BREAK_SHORTCUT = Shortcut("session.short_break", "<control>2") LONG_BREAK_SHORTCUT = Shortcut("session.long_break", "<control>3") @inject( bus="tomate.bus", session="tomate.session", shortcuts="tomate.ui.shortcut", ) def __init__(self, bus: Bus, session: Session, shortcuts: ShortcutEngine): self.connect(bus) self._session = session self._shortcuts = shortcuts self.widget = self._create_mode_button() self._add_button(SessionButton.POMODORO_SHORTCUT, "Pomodoro", SessionType.POMODORO) self._add_button(SessionButton.SHORT_BREAK_SHORTCUT, "Short Break", SessionType.SHORT_BREAK) self._add_button(SessionButton.LONG_BREAK_SHORTCUT, "Long Break", SessionType.LONG_BREAK) self.widget.connect("mode_changed", self._on_button_clicked) def _create_mode_button(self) -> ModeButton: return ModeButton( can_focus=False, homogeneous=True, margin_bottom=12, margin_left=12, margin_right=12, sensitive=True, ) def _add_button(self, shortcut: Shortcut, label: str, session_type: SessionType) -> None: self.widget.append_text( _(label), tooltip_text=_("{} ({})".format(label, self._shortcuts.label(shortcut))), name=shortcut.name, ) self._shortcuts.connect(shortcut, self._change_session(session_type)) def _change_session(self, session_type: SessionType) -> Callable[[], bool]: def callback(*_) -> bool: logger.debug("action=change session=%s", session_type) self.widget.set_selected(session_type.value) return True return callback def _on_button_clicked(self, _, number): session_type = SessionType.of(number) logger.debug("action=mode_changed session=%s", session_type) self._session.change(session_type) @on(Events.SESSION_CHANGE) def _on_session_change(self, payload=SessionPayload) -> None: logger.debug("action=change current=%d next=%d", self.widget.get_selected(), payload.type.value) if self.widget.get_selected() != payload.type.value: self.widget.set_selected(payload.type.value) @on(Events.SESSION_START) def _on_session_start(self, **__): logger.debug("action=disable") self.widget.props.sensitive = False @on(Events.SESSION_READY, Events.SESSION_INTERRUPT, Events.SESSION_END) def _on_session_stop(self, payload: Union[SessionPayload, SessionEndPayload]): logger.debug("action=enable session=%s", payload.type) self.widget.props.sensitive = True self.widget.set_selected(payload.type.value)
class HeaderBar(Subscriber): START_SHORTCUT = Shortcut("session.start", "<control>s") STOP_SHORTCUT = Shortcut("session.stop", "<control>p") RESET_SHORTCUT = Shortcut("session.reset", "<control>r") @inject( bus="tomate.bus", menu="tomate.ui.headerbar.menu", session="tomate.session", shortcuts="tomate.ui.shortcut", ) def __init__(self, bus: Bus, menu: Menu, session: Session, shortcuts: ShortcutEngine): self.connect(bus) self._shortcuts = shortcuts self.widget = self._create_headerbar() self._start_button = self._add_button( Gtk.STOCK_MEDIA_PLAY, "Starts the session", HeaderBar.START_SHORTCUT, lambda *_: session.start(), ) self._stop_button = self._add_button( Gtk.STOCK_MEDIA_STOP, "Stops the session", HeaderBar.STOP_SHORTCUT, lambda *_: session.stop(), no_show_all=True, visible=False, ) self._reset_button = self._add_button( Gtk.STOCK_CLEAR, "Clear session count", HeaderBar.RESET_SHORTCUT, lambda *_: session.reset(), sensitive=False, ) self._add_preference_button(menu, shortcuts) def _create_headerbar(self): return Gtk.HeaderBar( show_close_button=True, title=_("No session yet"), decoration_layout=":close", ) def _add_button(self, icon: str, tooltip_text: str, shortcut: Shortcut, on_clicked, **props) -> Gtk.Button: image = Gtk.Image.new_from_icon_name(icon, Gtk.IconSize.BUTTON) image.show() button = Gtk.Button( tooltip_text=_("{} ({})".format(tooltip_text, self._shortcuts.label(shortcut))), name=shortcut.name, **props ) button.add(image) button.connect("clicked", on_clicked) self.widget.pack_start(button) self._shortcuts.connect(shortcut, on_clicked) return button def _add_preference_button(self, menu, shortcuts) -> None: icon = Gtk.Image.new_from_icon_name(Gtk.STOCK_PREFERENCES, Gtk.IconSize.BUTTON) button = Gtk.MenuButton( name=Menu.PREFERENCE_SHORTCUT.name, popup=menu.widget, tooltip_text=_("Open preferences ({})".format(shortcuts.label(Menu.PREFERENCE_SHORTCUT))), ) button.add(icon) self.widget.pack_end(button) @on(Events.SESSION_START) def _on_session_start(self, **__): logger.debug("action=enable_stop") self._start_button.props.visible = False self._stop_button.props.visible = True self._reset_button.props.sensitive = False @on(Events.SESSION_INTERRUPT, Events.SESSION_END) def _on_session_stop(self, payload: Union[SessionEndPayload, SessionPayload]) -> None: logger.debug("action=enable_start pomodoros=%d", payload.pomodoros) self._start_button.props.visible = True self._stop_button.props.visible = False self._reset_button.props.sensitive = bool(payload.pomodoros) self._update_title(payload.pomodoros) @on(Events.SESSION_RESET) def _on_session_reset(self, **__): logger.debug("action=disable_reset") self._reset_button.props.sensitive = False self._update_title(0) def _update_title(self, pomodoros: int) -> None: self.widget.props.title = _("Session {}".format(pomodoros)) if pomodoros else _("No session yet")
def test_label_with_fallback(shortcut_engine): label = shortcut_engine.label(Shortcut("", "<control>p")) assert label == "Ctrl+P"
def test_label(shortcut_engine): label = shortcut_engine.label(Shortcut("test", "")) assert label == "Ctrl+S"