Anna Schumaker
710f3fba80
And style them so the row is highlighted and the text is bold. Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
197 lines
7.4 KiB
Python
197 lines
7.4 KiB
Python
# Copyright 2022 (c) Anna Schumaker.
|
|
"""A customized Gtk.SignalListItemFactory for easier use."""
|
|
import typing
|
|
from gi.repository import GObject
|
|
from gi.repository import Gio
|
|
from gi.repository import Gtk
|
|
|
|
|
|
class ListRow(GObject.GObject):
|
|
"""Extra state that we attach to the Gtk.ListItem."""
|
|
|
|
listitem = GObject.Property(type=Gtk.ListItem)
|
|
|
|
def __init__(self, listitem: Gtk.ListItem, **kwargs):
|
|
"""Initialize a ListRow object."""
|
|
GObject.GObject.__init__(self, listitem=listitem, **kwargs)
|
|
self.bindings = []
|
|
|
|
def do_bind(self) -> None:
|
|
"""Bind the list item to the child widget."""
|
|
|
|
def do_unbind(self) -> None:
|
|
"""Unbind the list item from the child widget."""
|
|
|
|
def bind_active(self, item_prop: str) -> None:
|
|
"""Bind a property to the Row's active property."""
|
|
self.bind_and_set(self.item, item_prop, self, "active")
|
|
|
|
def bind_and_set(self, src: GObject.GObject, src_prop: str,
|
|
dst: GObject.GObject, dst_prop: str,
|
|
bidirectional: bool = False,
|
|
invert_boolean: bool = False) -> None:
|
|
"""Bind and set a property from the src object to the dst object."""
|
|
f_bidir = GObject.BindingFlags.BIDIRECTIONAL if bidirectional else 0
|
|
f_invrt = GObject.BindingFlags.INVERT_BOOLEAN if invert_boolean else 0
|
|
|
|
src_value = src.get_property(src_prop)
|
|
value = not src_value if invert_boolean else src_value
|
|
dst.set_property(dst_prop, value)
|
|
self.bindings.append(src.bind_property(src_prop, dst, dst_prop,
|
|
f_bidir | f_invrt))
|
|
|
|
def bind_and_set_property(self, item_prop: str, child_prop: str,
|
|
bidirectional: bool = False,
|
|
invert_boolean: bool = False) -> None:
|
|
"""Bind and set a list item property."""
|
|
self.bind_and_set(self.item, item_prop, self.child, child_prop,
|
|
bidirectional, invert_boolean)
|
|
|
|
def bind(self) -> None:
|
|
"""Bind the list item to the child widget."""
|
|
self.do_bind()
|
|
|
|
def unbind(self) -> None:
|
|
"""Unbind the list item from the child widget."""
|
|
for binding in self.bindings:
|
|
binding.unbind()
|
|
self.bindings.clear()
|
|
self.do_unbind()
|
|
|
|
@GObject.Property(type=bool, default=False)
|
|
def active(self) -> bool:
|
|
"""Get the active state of this Row."""
|
|
if parent := self.listitem.get_child().get_parent():
|
|
return parent.get_state_flags() & Gtk.StateFlags.CHECKED
|
|
return False
|
|
|
|
@active.setter
|
|
def active(self, newval: bool) -> None:
|
|
if parent := self.listitem.get_child().get_parent():
|
|
if newval:
|
|
parent.set_state_flags(Gtk.StateFlags.CHECKED, False)
|
|
else:
|
|
parent.unset_state_flags(Gtk.StateFlags.CHECKED)
|
|
|
|
@GObject.Property(type=Gtk.Widget)
|
|
def child(self) -> Gtk.Widget | None:
|
|
"""Get the child widget displayed by this Row."""
|
|
return self.listitem.get_child()
|
|
|
|
@child.setter
|
|
def child(self, newval: Gtk.Widget) -> None:
|
|
self.listitem.set_child(newval)
|
|
|
|
@GObject.Property(type=GObject.TYPE_PYOBJECT)
|
|
def item(self) -> GObject.TYPE_PYOBJECT:
|
|
"""Get the list item for this Row."""
|
|
return self.listitem.get_item()
|
|
|
|
|
|
class InscriptionRow(ListRow):
|
|
"""A ListRow for displaying Gtk.Inscription widgets."""
|
|
|
|
item_property = GObject.Property(type=str)
|
|
|
|
def __init__(self, listitem: Gtk.ListItem, item_property: str,
|
|
xalign: float = 0.0, numeric: bool = False) -> None:
|
|
"""Create a new Gtk.Label."""
|
|
super().__init__(listitem, item_property=item_property)
|
|
self.child = Gtk.Inscription(xalign=xalign)
|
|
if numeric:
|
|
self.child.add_css_class("numeric")
|
|
|
|
def do_bind(self) -> None:
|
|
"""Bind a ListItem to the Label."""
|
|
self.bind_and_set_property(self.item_property, "text")
|
|
|
|
|
|
class TreeRow(ListRow):
|
|
"""A ListRow for displaying child widgets in a Tree."""
|
|
|
|
n_children = GObject.Property(type=int)
|
|
have_children = GObject.Property(type=bool, default=False)
|
|
indented = GObject.Property(type=bool, default=True)
|
|
|
|
def __init__(self, listitem: Gtk.ListItem, **kwargs) -> None:
|
|
"""Create a new TreeRow."""
|
|
super().__init__(listitem, **kwargs)
|
|
listitem.set_child(Gtk.TreeExpander(hide_expander=True,
|
|
indent_for_icon=self.indented))
|
|
self.bind_property("n-children", self, "have-children")
|
|
self.bind_property("have-children", listitem.get_child(),
|
|
"hide-expander",
|
|
GObject.BindingFlags.INVERT_BOOLEAN)
|
|
self.bind_property("indented", listitem.get_child(), "indent-for-icon")
|
|
|
|
def bind(self) -> None:
|
|
"""Bind a TreeRow to the TreeExpander."""
|
|
self.listitem.get_child().set_list_row(self.listitem.get_item())
|
|
super().bind()
|
|
|
|
def bind_n_children(self, children: Gio.ListModel | None) -> None:
|
|
"""Bind to the n-items property of the child listmodel."""
|
|
if children is not None:
|
|
self.bind_and_set(children, "n-items", self, "n-children")
|
|
|
|
def unbind(self) -> None:
|
|
"""Unbind a TreeRow from the TreeExpander."""
|
|
self.listitem.get_child().set_list_row(None)
|
|
super().unbind()
|
|
|
|
@GObject.Property(type=Gtk.Widget)
|
|
def child(self) -> Gtk.Widget | None:
|
|
"""Get the child widget displayed by this Row."""
|
|
return self.listitem.get_child().get_child()
|
|
|
|
@child.setter
|
|
def child(self, newval=Gtk.Widget) -> None:
|
|
self.listitem.get_child().set_child(newval)
|
|
|
|
@GObject.Property(type=GObject.TYPE_PYOBJECT)
|
|
def item(self) -> GObject.TYPE_PYOBJECT:
|
|
"""Get the list item for this Row."""
|
|
return self.listitem.get_item().get_item()
|
|
|
|
|
|
class Factory(Gtk.SignalListItemFactory):
|
|
"""A customized Factory for making list row widgets."""
|
|
|
|
def __init__(self, row_type: typing.Type[ListRow], **kwargs):
|
|
"""Initialize a ListFactory."""
|
|
super().__init__()
|
|
self.row_type = row_type
|
|
|
|
self.connect("setup", self.__setup, kwargs)
|
|
self.connect("bind", self.__bind)
|
|
self.connect("unbind", self.__unbind)
|
|
self.connect("teardown", self.__teardown)
|
|
|
|
def __setup(self, factory: Gtk.SignalListItemFactory,
|
|
listitem: Gtk.ListItem, kwargs: dict) -> None:
|
|
listitem.listrow = self.row_type(listitem, **kwargs)
|
|
|
|
def __bind(self, factory: Gtk.SignalListItemFactory,
|
|
listitem: Gtk.ListItem) -> None:
|
|
listitem.listrow.bind()
|
|
|
|
def __unbind(self, factory: Gtk.SignalListItemFactory,
|
|
listitem: Gtk.ListItem) -> None:
|
|
listitem.listrow.unbind()
|
|
|
|
def __teardown(self, factory: Gtk.SignalListItemFactory,
|
|
listitem: Gtk.ListItem) -> None:
|
|
listitem.set_child(None)
|
|
listitem.listrow = None
|
|
|
|
|
|
class InscriptionFactory(Factory):
|
|
"""A Factory that creates InscriptionRows."""
|
|
|
|
def __init__(self, item_property: str,
|
|
script_type: typing.Type[InscriptionRow] = InscriptionRow,
|
|
xalign: float = 0.0, numeric: bool = False):
|
|
"""Initialize a LabelFactory."""
|
|
super().__init__(row_type=script_type, item_property=item_property,
|
|
xalign=xalign, numeric=numeric)
|