emmental/emmental/buttons.py
Anna Schumaker eb6b4d8ef4 buttons: Watch for ImageToggle tooltip text changes
If the application changes the active or inactive tooltip text, then we
want to apply that to the button depending on what state it currently
has.

Signed-off-by: Anna Schumaker <Anna@NoWheyCreamery.com>
2023-08-22 13:29:53 -04:00

153 lines
5.6 KiB
Python

# Copyright 2022 (c) Anna Schumaker.
"""Helper classes for Buttons."""
from gi.repository import GObject
from gi.repository import Gtk
class Button(Gtk.Button):
"""A Gtk.Button with extra properties and default large size."""
icon_name = GObject.Property(type=str)
icon_opacity = GObject.Property(type=float, default=1.0,
minimum=0.0, maximum=1.0)
def __init__(self, large_icon: bool = False, **kwargs):
"""Initialize a Button."""
super().__init__(focusable=False, **kwargs)
icon_size = Gtk.IconSize.LARGE if large_icon else Gtk.IconSize.NORMAL
self._image = Gtk.Image(icon_name=self.icon_name, icon_size=icon_size,
opacity=self.icon_opacity)
self.bind_property("icon-name", self._image, "icon-name")
self.bind_property("icon-opacity", self._image, "opacity")
self.set_child(self._image)
@GObject.Property(type=bool, default=False)
def large_icon(self) -> bool:
"""Get if this Button has a large icon."""
return self._image.get_icon_size() == Gtk.IconSize.LARGE
@large_icon.setter
def large_icon(self, newval: bool) -> None:
size = Gtk.IconSize.LARGE if newval else Gtk.IconSize.NORMAL
self._image.set_icon_size(size)
class PopoverButton(Gtk.MenuButton):
"""A MenuButton with a Gtk.Popover attached."""
popover_child = GObject.Property(type=Gtk.Widget)
def __init__(self, **kwargs):
"""Initialize a popover.Button."""
super().__init__(popover=Gtk.Popover(), **kwargs)
self.bind_property("popover-child", self.get_popover(), "child")
self.get_popover().set_child(self.popover_child)
def popdown(self):
"""Close the popover."""
self.get_popover().popdown()
class SplitButton(Gtk.Box):
"""A Button and secondary widget packed together."""
icon_name = GObject.Property(type=str)
large_icon = GObject.Property(type=bool, default=False)
def __init__(self, secondary: Gtk.Button, **kwargs):
"""Initialize a Split Button."""
super().__init__(**kwargs)
self._primary = Button(hexpand=True, icon_name=self.icon_name,
large_icon=self.large_icon)
self._separator = Gtk.Separator(orientation=Gtk.Orientation.VERTICAL,
margin_top=12, margin_bottom=12)
self._secondary = secondary
self.bind_property("icon-name", self._primary, "icon-name")
self.bind_property("large-icon", self._primary, "large-icon")
self._primary.connect("activate", self.__activate)
self._primary.connect("clicked", self.__clicked)
self.append(self._primary)
self.append(self._separator)
self.append(secondary)
self.add_css_class("emmental-splitbutton")
def __activate(self, button: Button) -> None:
self.emit("activate-primary")
def __clicked(self, button: Button) -> None:
self.emit("clicked")
def activate(self, *args) -> None:
"""Activate the primary button."""
self._primary.activate()
@GObject.Property(type=Gtk.Button, flags=GObject.ParamFlags.READABLE)
def secondary(self) -> Gtk.Button:
"""Get the secondary button attached to the SplitButton."""
return self._secondary
@GObject.Signal
def activate_primary(self) -> None:
"""Signal that the primary button has been activated."""
@GObject.Signal
def clicked(self) -> None:
"""Signal that the primary button has been clicked."""
class ImageToggle(Button):
"""Inspired by a ToggleButton, but changes image based on state."""
active_icon_name = GObject.Property(type=str)
active_tooltip_text = GObject.Property(type=str)
inactive_icon_name = GObject.Property(type=str)
inactive_tooltip_text = GObject.Property(type=str)
def __init__(self, active_icon_name: str, inactive_icon_name: str,
active_tooltip_text: str | None = None,
inactive_tooltip_text: str | None = None,
*, active: bool = False, **kwargs) -> None:
"""Initialize an ImageToggle button."""
super().__init__(active_icon_name=active_icon_name,
inactive_icon_name=inactive_icon_name,
icon_name=inactive_icon_name,
active_tooltip_text=active_tooltip_text,
inactive_tooltip_text=inactive_tooltip_text,
tooltip_text=inactive_tooltip_text,
active=active, **kwargs)
self.connect("notify", self.__notify)
def __notify(self, toggle: Button, param: GObject.ParamSpec) -> None:
match (param.name, self.active):
case ("active-tooltip-text", True) | \
("inactive-tooltip-text", False):
self.set_tooltip_text(self.get_property(param.name))
def do_clicked(self) -> None:
"""Handle a click event."""
self.active = not self.active
@GObject.Property(type=bool, default=False)
def active(self) -> bool:
"""Get the active state."""
return self.icon_name == self.active_icon_name
@active.setter
def active(self, newval: bool) -> None:
if newval != self.active:
if newval:
self.icon_name = self.active_icon_name
self.props.tooltip_text = self.active_tooltip_text
else:
self.icon_name = self.inactive_icon_name
self.props.tooltip_text = self.inactive_tooltip_text
self.emit("toggled")
@GObject.Signal
def toggled(self) -> None:
"""Active state has been toggled."""