sidebar: Create an EditableTitle widget
This widget gives users the ability to change the title of the displayed playlist. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
This commit is contained in:
parent
cdae9541e9
commit
673c6910e9
|
@ -46,3 +46,69 @@ class PlaylistTitle(Title):
|
|||
def __update_subtitle(self, title: Title, param) -> None:
|
||||
s_count = "s" if self.count != 1 else ""
|
||||
self.subtitle = f"{self.count} track{s_count}"
|
||||
|
||||
|
||||
class EditableTitle(Gtk.Stack):
|
||||
"""A Title widget and an Entry for handling renames."""
|
||||
|
||||
title = GObject.Property(type=str)
|
||||
count = GObject.Property(type=int)
|
||||
editable = GObject.Property(type=bool, default=False)
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
"""Initialize a row EditableTitle widget."""
|
||||
super().__init__(**kwargs)
|
||||
self._title = PlaylistTitle(title=self.title, count=self.count)
|
||||
self._entry = Gtk.Text(text=self.title)
|
||||
self._long_press = Gtk.GestureLongPress()
|
||||
self._trigger = Gtk.ShortcutTrigger.parse_string("Escape")
|
||||
self._action = Gtk.CallbackAction.new(self.__cancel_editing)
|
||||
self._cancel = Gtk.Shortcut.new(self._trigger, self._action)
|
||||
self._shortcut = Gtk.ShortcutController()
|
||||
|
||||
self._shortcut.add_shortcut(self._cancel)
|
||||
|
||||
self.bind_property("title", self._title, "title")
|
||||
self.bind_property("title", self._entry, "text")
|
||||
self.bind_property("count", self._title, "count")
|
||||
|
||||
self._long_press.connect("pressed", self.__long_press)
|
||||
self._entry.connect("activate", self.__entry_activated)
|
||||
self._entry.connect("notify::has-focus", self.__focus_change)
|
||||
|
||||
self.add_child(self._title)
|
||||
self.add_child(self._entry)
|
||||
self.add_controller(self._long_press)
|
||||
self.add_controller(self._shortcut)
|
||||
self.add_css_class("linked")
|
||||
|
||||
def __long_press(self, gesture: Gtk.GestureLongPress,
|
||||
x: float, y: float) -> None:
|
||||
self.editing = True
|
||||
|
||||
def __entry_activated(self, entry: Gtk.Text) -> None:
|
||||
self.emit("request-rename", entry.get_text())
|
||||
|
||||
def __focus_change(self, text: Gtk.Text, param) -> None:
|
||||
self.editing = text.has_focus()
|
||||
|
||||
def __cancel_editing(self, title, *args) -> None:
|
||||
self.editing = False
|
||||
|
||||
@GObject.Property
|
||||
def editing(self) -> bool:
|
||||
"""Get if we are currently editing the label."""
|
||||
return self.get_visible_child() == self._entry
|
||||
|
||||
@editing.setter
|
||||
def editing(self, newval: bool) -> None:
|
||||
if self.editable and newval:
|
||||
self.set_visible_child(self._entry)
|
||||
self._entry.grab_focus()
|
||||
else:
|
||||
self.set_visible_child(self._title)
|
||||
|
||||
@GObject.Signal(arg_types=(str,))
|
||||
def request_rename(self, new_name: str):
|
||||
"""Signal that the user has entered new text."""
|
||||
self.editing = False
|
||||
|
|
|
@ -87,3 +87,92 @@ class TestPlaylistTitle(unittest.TestCase):
|
|||
|
||||
self.title.count = 2
|
||||
self.assertEqual(self.title.subtitle, "2 tracks")
|
||||
|
||||
|
||||
class TestEditableTitle(unittest.TestCase):
|
||||
"""Tests the editable title widget."""
|
||||
|
||||
def setUp(self):
|
||||
"""Set up common variables."""
|
||||
self.title = emmental.sidebar.title.EditableTitle()
|
||||
|
||||
def test_init(self):
|
||||
"""Test that the editable title widget is set up properly."""
|
||||
self.assertIsInstance(self.title, Gtk.Stack)
|
||||
self.assertIsNotNone(self.title.get_page(self.title._title))
|
||||
self.assertIsNotNone(self.title.get_page(self.title._entry))
|
||||
|
||||
def test_playlist_title(self):
|
||||
"""Test the playlist title child."""
|
||||
self.assertIsInstance(self.title._title,
|
||||
emmental.sidebar.title.PlaylistTitle)
|
||||
self.assertEqual(self.title.title, "")
|
||||
self.assertEqual(self.title.count, 0)
|
||||
|
||||
self.title.title = "Test Title"
|
||||
self.assertEqual(self.title._title.title, "Test Title")
|
||||
self.assertEqual(self.title._entry.get_text(), "Test Title")
|
||||
|
||||
self.title.count = 42
|
||||
self.assertEqual(self.title._title.count, 42)
|
||||
|
||||
title2 = emmental.sidebar.title.EditableTitle(title="Test Title")
|
||||
self.assertEqual(title2._title.title, "Test Title")
|
||||
self.assertEqual(title2._entry.get_text(), "Test Title")
|
||||
|
||||
def test_visible_child(self):
|
||||
"""Test switching to the Entry for editing."""
|
||||
self.assertEqual(self.title.get_visible_child(), self.title._title)
|
||||
self.assertFalse(self.title.editing)
|
||||
|
||||
self.title.editing = True
|
||||
self.assertFalse(self.title.editing)
|
||||
|
||||
self.title.editable = True
|
||||
self.title.editing = True
|
||||
self.assertTrue(self.title.editing)
|
||||
self.assertEqual(self.title.get_visible_child(), self.title._entry)
|
||||
|
||||
self.title.editing = False
|
||||
self.assertEqual(self.title.get_visible_child(), self.title._title)
|
||||
|
||||
def test_cancel(self):
|
||||
"""Test the cancel Shortcut for editing."""
|
||||
self.assertIsInstance(self.title._shortcut, Gtk.ShortcutController)
|
||||
self.assertIsInstance(self.title._cancel, Gtk.Shortcut)
|
||||
self.assertIsInstance(self.title._trigger, Gtk.KeyvalTrigger)
|
||||
self.assertIsInstance(self.title._action, Gtk.CallbackAction)
|
||||
|
||||
self.assertEqual(self.title._cancel.get_trigger(), self.title._trigger)
|
||||
self.assertEqual(self.title._cancel.get_action(), self.title._action)
|
||||
|
||||
self.assertEqual(self.title._trigger.get_keyval(),
|
||||
Gtk.accelerator_parse("Escape").accelerator_key)
|
||||
self.assertEqual(self.title._trigger.get_modifiers(), 0)
|
||||
|
||||
self.assertIn(self.title._cancel, self.title._shortcut)
|
||||
self.assertIn(self.title._shortcut, self.title.observe_controllers())
|
||||
|
||||
self.title.editing = True
|
||||
self.title._action.activate(0, self.title, None)
|
||||
self.assertFalse(self.title.editing)
|
||||
|
||||
def test_editing(self):
|
||||
"""Test editing the title label."""
|
||||
self.assertIsInstance(self.title._entry, Gtk.Text)
|
||||
self.assertIsInstance(self.title._long_press, Gtk.GestureLongPress)
|
||||
self.assertIn(self.title._long_press, self.title.observe_controllers())
|
||||
|
||||
self.title._long_press.emit("pressed", 0, 0)
|
||||
self.assertFalse(self.title.editing)
|
||||
|
||||
self.title.editable = True
|
||||
self.title._long_press.emit("pressed", 0, 0)
|
||||
self.assertTrue(self.title.editing)
|
||||
|
||||
callback = unittest.mock.Mock()
|
||||
self.title.connect("request-rename", callback)
|
||||
self.title._entry.set_text("New Name")
|
||||
self.title._entry.emit("activate")
|
||||
self.assertFalse(self.title.editing)
|
||||
callback.assert_called_with(self.title, "New Name")
|
||||
|
|
Loading…
Reference in New Issue