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