emmental/emmental/buttons.py

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."""