Showing placeholder text (#658)

pull/669/head
Tobi 1 year ago committed by GitHub
parent d0cc1b82a8
commit 6b4b85357f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

@ -55,5 +55,5 @@ if __name__ == '__main__':
atexit.register(on_exit)
groups = _Groups()
reader_service = ReaderService(groups)
loop = asyncio.get_event_loop()
loop = asyncio.new_event_loop()
loop.run_until_complete(reader_service.run())

@ -37,6 +37,12 @@
background: @theme_bg_color;
}
.opaque-text text {
/* found by roaming through /usr/share/themes,
and some experimentation in the gnome inspector */
color: alpha(currentColor, 0.5);
}
/*
@theme_bg_color
@theme_selected_bg_color

@ -31,7 +31,6 @@ from evdev.ecodes import (
EV_KEY,
EV_ABS,
EV_REL,
bytype,
BTN_LEFT,
BTN_MIDDLE,
BTN_RIGHT,
@ -402,6 +401,8 @@ class GdkEventRecorder:
class CodeEditor:
"""The editor used to edit the output_symbol of the active_mapping."""
placeholder: str = _("Enter your output here")
def __init__(
self,
message_broker: MessageBroker,
@ -428,22 +429,52 @@ class CodeEditor:
# TODO there are some similarities with python, but overall it's quite useless.
# commented out until there is proper highlighting for input-remappers syntax.
# todo: setup autocompletion here
self._update_placeholder()
self.gui.get_buffer().connect("changed", self._on_gtk_changed)
self.gui.connect("focus-in-event", self._update_placeholder)
self.gui.connect("focus-out-event", self._update_placeholder)
self._connect_message_listener()
def _update_placeholder(self, *_):
buffer = self.gui.get_buffer()
code = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)
# test for incorrect states and fix them, without causing side effects
with HandlerDisabled(buffer, self._on_gtk_changed):
if self.gui.has_focus() and code == self.placeholder:
# hide the placeholder
buffer.set_text("")
self.gui.get_style_context().remove_class("opaque-text")
elif code == "":
# show the placeholder instead
buffer.set_text(self.placeholder)
self.gui.get_style_context().add_class("opaque-text")
elif code != "":
# something is written, ensure the opacity is correct
self.gui.get_style_context().remove_class("opaque-text")
def _shows_placeholder(self):
buffer = self.gui.get_buffer()
code = buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)
return code == self.placeholder
@property
def code(self) -> str:
"""Get the user-defined macro code string."""
if self._shows_placeholder():
return ""
buffer = self.gui.get_buffer()
return buffer.get_text(buffer.get_start_iter(), buffer.get_end_iter(), True)
@code.setter
def code(self, code: str) -> None:
"""Set the text without triggering any events."""
buffer = self.gui.get_buffer()
with HandlerDisabled(buffer, self._on_gtk_changed):
buffer.set_text(code)
self._update_placeholder()
self.gui.do_move_cursor(self.gui, Gtk.MovementStep.BUFFER_ENDS, -1, False)
def _connect_message_listener(self):
@ -471,6 +502,9 @@ class CodeEditor:
self.gui.get_style_context().remove_class("multiline")
def _on_gtk_changed(self, *_):
if self._shows_placeholder():
return
self._controller.update_mapping(output_symbol=self.code)
def _on_mapping_loaded(self, mapping: MappingData):

@ -20,7 +20,7 @@
import unittest
from typing import Optional, Tuple, Union
from unittest.mock import MagicMock
from unittest.mock import MagicMock, call
import time
import evdev
@ -867,15 +867,22 @@ class TestCodeEditor(ComponentBaseTest):
self.message_broker.publish(MappingData(output_symbol="foo"))
self.assertFalse(self.gui.get_show_line_numbers())
def test_is_empty_when_mapping_has_no_output_symbol(self):
def test_shows_placeholder_when_mapping_has_no_output_symbol(self):
self.message_broker.publish(MappingData())
self.assertEqual(self.get_text(), "")
self.assertEqual(self.get_text(), self.editor.placeholder)
# there are no side-effects because the placeholder is inserted:
self.controller_mock.update_mapping.assert_not_called()
def test_updates_mapping(self):
self.message_broker.publish(MappingData())
buffer = self.gui.get_buffer()
self.controller_mock.update_mapping.assert_not_called()
buffer.set_text("foo")
self.controller_mock.update_mapping.assert_called_once_with(output_symbol="foo")
call_args_list = self.controller_mock.update_mapping.call_args_list
# this test emits 2 events for whatever reason, the first one with an empty
# symbol. this doesn't actually seem to happen when using it.
self.assertEqual(call_args_list[-1], call(output_symbol="foo"))
def test_avoids_infinite_recursion_when_loading_mapping(self):
self.message_broker.publish(MappingData(output_symbol="foo"))
@ -885,6 +892,41 @@ class TestCodeEditor(ComponentBaseTest):
self.message_broker.signal(MessageType.recording_finished)
self.controller_mock.set_focus.assert_called_once_with(self.gui)
def test_placeholder(self):
self.assertEqual(self.get_text(), self.editor.placeholder)
window = Gtk.Window()
window.add(self.gui)
window.show_all()
def focus():
self.gui.grab_focus()
gtk_iteration(5)
def unfocus():
window.set_focus(None)
gtk_iteration(5)
# clears the input when we enter the editor widget
focus()
self.assertEqual(self.get_text(), "")
self.assertNotIn("opaque-text", self.gui.get_style_context().list_classes())
# adds the placeholder back when we leave it
unfocus()
self.assertEqual(self.get_text(), self.editor.placeholder)
self.assertIn("opaque-text", self.gui.get_style_context().list_classes())
# if we enter text and then leave, it won't show the placeholder
focus()
self.assertEqual(self.get_text(), "")
buffer = self.gui.get_buffer()
buffer.set_text("foo")
self.assertNotIn("opaque-text", self.gui.get_style_context().list_classes())
unfocus()
self.assertEqual(self.get_text(), "foo")
self.assertNotIn("opaque-text", self.gui.get_style_context().list_classes())
class TestRecordingToggle(ComponentBaseTest):
def setUp(self) -> None:

Loading…
Cancel
Save