diff --git a/keymapper/gtk/row.py b/keymapper/gtk/row.py
index ab3ea155..3d1649c4 100644
--- a/keymapper/gtk/row.py
+++ b/keymapper/gtk/row.py
@@ -95,12 +95,8 @@ class Row(Gtk.ListBoxRow):
def get_keycode(self):
"""Get a tuple of type, code and value from the left column.
- Or None if no codes are mapped on this row.
+ Or None if no code is mapped on this row.
"""
- keycode = self.keycode_input.get_label()
- if not keycode:
- return None
-
return self.key
def get_character(self):
@@ -185,6 +181,32 @@ class Row(Gtk.ListBoxRow):
value = store.get_value(tree_iter, 0)
return key in value.lower()
+ def show_click_here(self):
+ """Show 'click here' on the keycode input button."""
+ if self.get_keycode() is not None:
+ return
+
+ self.keycode_input.set_label('click here')
+ self.keycode_input.set_opacity(0.3)
+
+ def show_press_key(self):
+ """Show 'press key' on the keycode input button."""
+ if self.get_keycode() is not None:
+ return
+
+ self.keycode_input.set_label('press key')
+ self.keycode_input.set_opacity(1)
+
+ def keycode_input_focus(self, *args):
+ """Refresh useful usage information."""
+ self.show_press_key()
+ self.window.can_modify_mapping()
+
+ def keycode_input_unfocus(self, *args):
+ """Refresh useful usage information."""
+ self.show_click_here()
+ self.keycode_input.set_active(False)
+
def put_together(self, character):
"""Create all child GTK widgets and connect their signals."""
delete_button = Gtk.EventBox()
@@ -199,23 +221,27 @@ class Row(Gtk.ListBoxRow):
delete_button.set_size_request(50, -1)
keycode_input = Gtk.ToggleButton()
+ self.keycode_input = keycode_input
keycode_input.set_size_request(140, -1)
if self.key is not None:
keycode_input.set_label(to_string(*self.key))
+ else:
+ self.show_click_here()
# make the togglebutton go back to its normal state when doing
# something else in the UI
keycode_input.connect(
'focus-in-event',
- self.window.can_modify_mapping
+ self.keycode_input_focus
)
keycode_input.connect(
'focus-out-event',
- lambda *args: keycode_input.set_active(False)
+ self.keycode_input_unfocus
)
character_input = Gtk.Entry()
+ self.character_input = character_input
character_input.set_alignment(0.5)
character_input.set_width_chars(4)
character_input.set_has_frame(False)
@@ -245,9 +271,6 @@ class Row(Gtk.ListBoxRow):
self.add(box)
self.show_all()
- self.character_input = character_input
- self.keycode_input = keycode_input
-
def on_delete_button_clicked(self, *args):
"""Destroy the row and remove it from the config."""
key = self.get_keycode()
@@ -256,4 +279,5 @@ class Row(Gtk.ListBoxRow):
self.character_input.set_text('')
self.keycode_input.set_label('')
+ self.key = None
self.delete_callback(self)
diff --git a/keymapper/gtk/window.py b/keymapper/gtk/window.py
index 08b7b67e..5a90a092 100755
--- a/keymapper/gtk/window.py
+++ b/keymapper/gtk/window.py
@@ -405,7 +405,11 @@ class Window:
logger.debug('Applying preset "%s" for "%s"', preset, device)
if custom_mapping.changed:
- self.show_status(CTX_APPLY, f'Applied outdated preset "{preset}"')
+ self.show_status(
+ CTX_APPLY,
+ f'Applied outdated preset "{preset}"',
+ 'Click "Save" first for changes to take effect'
+ )
else:
self.show_status(CTX_APPLY, f'Applied preset "{preset}"')
diff --git a/readme/pylint.svg b/readme/pylint.svg
index bfc215bd..bde3a939 100644
--- a/readme/pylint.svg
+++ b/readme/pylint.svg
@@ -17,7 +17,7 @@
pylint
- 9.77
- 9.77
+ 9.76
+ 9.76
\ No newline at end of file
diff --git a/readme/screenshot.png b/readme/screenshot.png
index ece82c77..01bf7097 100644
Binary files a/readme/screenshot.png and b/readme/screenshot.png differ
diff --git a/readme/screenshot_2.png b/readme/screenshot_2.png
index c0c0d944..ee9d9fd4 100644
Binary files a/readme/screenshot_2.png and b/readme/screenshot_2.png differ
diff --git a/tests/testcases/test_integration.py b/tests/testcases/test_integration.py
index 38d7f759..f673bb63 100644
--- a/tests/testcases/test_integration.py
+++ b/tests/testcases/test_integration.py
@@ -242,7 +242,7 @@ class TestIntegration(unittest.TestCase):
row.set_new_keycode(None)
self.assertIsNone(row.get_keycode())
self.assertEqual(len(custom_mapping), 0)
- self.assertEqual(row.keycode_input.get_label(), None)
+ self.assertEqual(row.keycode_input.get_label(), 'click here')
row.set_new_keycode((EV_KEY, 30, 1))
self.assertEqual(len(custom_mapping), 0)
@@ -294,7 +294,7 @@ class TestIntegration(unittest.TestCase):
# find the empty row
rows = self.get_rows()
row = rows[-1]
- self.assertIsNone(row.keycode_input.get_label())
+ self.assertIsNone(row.get_keycode())
self.assertEqual(row.character_input.get_text(), '')
self.assertNotIn('changed', row.get_style_context().list_classes())
@@ -304,7 +304,15 @@ class TestIntegration(unittest.TestCase):
row.character_input.set_text(char)
self.assertEqual(row.get_character(), char)
+ if row.keycode_input.is_focus():
+ self.assertEqual(row.keycode_input.get_label(), 'press key')
+ else:
+ self.assertEqual(row.keycode_input.get_label(), 'click here')
+
self.window.window.set_focus(row.keycode_input)
+ gtk_iteration()
+ self.assertIsNone(row.get_keycode())
+ self.assertEqual(row.keycode_input.get_label(), 'press key')
if key:
# modifies the keycode in the row not by writing into the input,
@@ -316,6 +324,7 @@ class TestIntegration(unittest.TestCase):
self.assertEqual(row.get_keycode(), key)
css_classes = row.get_style_context().list_classes()
self.assertIn('changed', css_classes)
+ self.assertEqual(row.keycode_input.get_label(), to_string(*key))
if not expect_success:
self.assertIsNone(row.get_keycode())
@@ -419,7 +428,7 @@ class TestIntegration(unittest.TestCase):
def test_remove_row(self):
"""Comprehensive test for rows 2."""
- # sleeps are added to be able to visually follow and debug the test
+ # sleeps are added to be able to visually follow and debug the test.
# add two rows by modifiying the one empty row that exists
row_1 = self.change_empty_row((EV_KEY, 10, 1), 'a')
row_2 = self.change_empty_row((EV_KEY, 11, 1), 'b')
@@ -433,6 +442,18 @@ class TestIntegration(unittest.TestCase):
self.assertEqual(custom_mapping.get_character((EV_KEY, 11, 1)), 'b')
def remove(row, code, char, num_rows_after):
+ """Remove a row by clicking the delete button.
+
+ Parameters
+ ----------
+ row : Row
+ code : int or None
+ keycode of the mapping that is displayed by this row
+ char : string or None
+ ouptut of the mapping that is displayed by this row
+ num_rows_after : int
+ after deleting, how many rows are expected to still be there
+ """
if code is not None and char is not None:
self.assertEqual(custom_mapping.get_character((EV_KEY, code, 1)), char)
@@ -441,9 +462,14 @@ class TestIntegration(unittest.TestCase):
self.assertIsNone(row.get_keycode())
else:
self.assertEqual(row.get_keycode(), (EV_KEY, code, 1))
+
row.on_delete_button_clicked()
time.sleep(0.2)
gtk_iteration()
+
+ # if a reference to the row is held somewhere and it is
+ # accidentally used again, make sure to not provide any outdated
+ # information that is supposed to be deleted
self.assertIsNone(row.get_keycode())
self.assertIsNone(row.get_character())
self.assertIsNone(custom_mapping.get_character((EV_KEY, code, 1)))