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)))