debouncing saving when typing

pull/551/head
sezanzeb 2 years ago
parent d2fc5a6c05
commit 492e06ef95

@ -27,6 +27,18 @@ from collections import defaultdict
from typing import List, Optional, Dict, Union, Callable, Literal, Set
import cairo
from evdev.ecodes import (
EV_KEY,
EV_ABS,
EV_REL,
bytype,
BTN_LEFT,
BTN_MIDDLE,
BTN_RIGHT,
BTN_EXTRA,
BTN_SIDE,
)
import gi
from evdev.ecodes import (
EV_KEY,
@ -43,7 +55,7 @@ from evdev.ecodes import (
gi.require_version("Gdk", "3.0")
gi.require_version("Gtk", "3.0")
gi.require_version("GtkSource", "4")
from gi.repository import Gtk, GtkSource, Gdk
from gi.repository import Gtk, GtkSource, Gdk, GObject
from inputremapper.configs.mapping import MappingData
from inputremapper.event_combination import EventCombination
@ -59,7 +71,7 @@ from inputremapper.gui.messages.message_data import (
PresetData,
CombinationUpdate,
)
from inputremapper.gui.utils import HandlerDisabled, Colors
from inputremapper.gui.utils import HandlerDisabled, Colors, debounce, debounce_manager
from inputremapper.injection.mapping_handlers.axis_transform import Transformation
from inputremapper.input_event import InputEvent
from inputremapper.configs.system_mapping import system_mapping, XKB_KEYCODE_OFFSET
@ -473,10 +485,21 @@ class CodeEditor:
self.gui.set_monospace(False)
self.gui.get_style_context().remove_class("multiline")
def _on_gtk_focus_out(self, *_):
# This helps to keep the gui data up-to-date when changed-events are
# debounced
self._controller.update_mapping(output_symbol=self.code)
debounce_manager.stop(self, self._on_gtk_changed)
@debounce(750)
def _on_gtk_changed(self, *_):
# This triggers for each typed character, will cause disk-writes and writes
# tons of logs, so this is debounced a bit
self._controller.update_mapping(output_symbol=self.code)
def _on_mapping_loaded(self, mapping: MappingData):
debounce_manager.stop(self, self._on_gtk_changed)
code = SET_KEY_FIRST
if not self._controller.is_empty_mapping():
code = mapping.output_symbol or ""
@ -487,6 +510,7 @@ class CodeEditor:
self._toggle_line_numbers()
def _on_recording_finished(self, _):
debounce_manager.stop(self, self._on_gtk_changed)
self._controller.set_focus(self.gui)

@ -750,12 +750,46 @@ class TestCodeEditor(ComponentBaseTest):
self.message_broker.publish(MappingData())
self.assertEqual(self.get_text(), "")
def test_updates_mapping(self):
def test_updates_mapping_debounced(self):
self.message_broker.publish(MappingData())
buffer = self.gui.get_buffer()
buffer.set_text("foo")
# the call is debounced by quite a lot
self.controller_mock.update_mapping.assert_not_called()
time.sleep(0.76)
gtk_iteration()
self.controller_mock.update_mapping.assert_called_once_with(output_symbol="foo")
def test_updates_mapping_on_unfocus(self):
self.message_broker.publish(MappingData())
buffer = self.gui.get_buffer()
buffer.set_text("foo")
# not called due to the debouncing
self.controller_mock.update_mapping.assert_not_called()
self.gui.emit("focus-out-event", None)
# the debounced call will not fire anymore
time.sleep(0.76)
gtk_iteration()
self.controller_mock.update_mapping.assert_called_once_with(output_symbol="foo")
def test_wont_update_mapping_after_change(self):
self.message_broker.publish(MappingData())
buffer = self.gui.get_buffer()
buffer.set_text("foo")
# the editor should not update the mapping after it changed
self.message_broker.publish(MappingData())
# the debounced call will not fire anymore
time.sleep(0.76)
gtk_iteration()
self.controller_mock.update_mapping.assert_not_called()
def test_avoids_infinite_recursion_when_loading_mapping(self):
self.message_broker.publish(MappingData(output_symbol="foo"))
self.controller_mock.update_mapping.assert_not_called()

@ -823,6 +823,7 @@ class TestGui(GuiTestBase):
# 3. set the output symbol
self.code_editor.get_buffer().set_text("Shift_L")
debounce_manager.run_all_now()
gtk_iteration()
# the mapping and preset should be valid by now
@ -890,6 +891,7 @@ class TestGui(GuiTestBase):
self.throttle(40)
gtk_iteration()
self.code_editor.get_buffer().set_text(symbol)
debounce_manager.run_all_now()
gtk_iteration()
add_mapping(ev_1, "a")
@ -972,6 +974,7 @@ class TestGui(GuiTestBase):
self.throttle(40)
gtk_iteration()
self.code_editor.get_buffer().set_text(symbol)
debounce_manager.run_all_now()
gtk_iteration()
add_mapping(combination_1, "a")
@ -1248,6 +1251,7 @@ class TestGui(GuiTestBase):
self.throttle(40)
gtk_iteration()
self.code_editor.get_buffer().set_text(symbol)
debounce_manager.run_all_now()
gtk_iteration()
combination = EventCombination(((EV_KEY, KEY_LEFTSHIFT, 1), (EV_KEY, 82, 1)))
@ -1277,6 +1281,7 @@ class TestGui(GuiTestBase):
with patch.object(self.data_manager.active_preset, "save", save):
self.code_editor.get_buffer().set_text("f")
debounce_manager.run_all_now()
gtk_iteration()
status = self.get_status_text()
self.assertIn("Permission denied", status)
@ -1335,12 +1340,14 @@ class TestGui(GuiTestBase):
warning_icon = self.user_interface.get("warning_status_icon")
self.code_editor.get_buffer().set_text("k(1))")
debounce_manager.run_all_now()
tooltip = status.get_tooltip_text().lower()
self.assertIn("brackets", tooltip)
self.assertTrue(error_icon.get_visible())
self.assertFalse(warning_icon.get_visible())
self.code_editor.get_buffer().set_text("k(1)")
debounce_manager.run_all_now()
tooltip = (status.get_tooltip_text() or "").lower()
self.assertNotIn("brackets", tooltip)
self.assertFalse(error_icon.get_visible())
@ -1363,6 +1370,7 @@ class TestGui(GuiTestBase):
# now change the mapping by typing into the field
buffer = self.code_editor.get_buffer()
buffer.set_text("sdfgkj()")
debounce_manager.run_all_now()
gtk_iteration()
# the mapping is validated

Loading…
Cancel
Save