Source code for sugar.graphics.radiotoolbutton

# Copyright (C) 2007, Red Hat, Inc.
# Copyright (C) 2025 MostlyK
#
# This library is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License as published by the Free Software Foundation; either
# version 2 of the License, or (at your option) any later version.
#
# This library is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public
# License along with this library; if not, write to the
# Free Software Foundation, Inc., 59 Temple Place - Suite 330,
# Boston, MA 02111-1307, USA.
#
# SPDX-License-Identifier: LGPL-2.1-or-later

"""
RadioToolButton

Provides a RadioToolButton class, similar to a "push" button.
A group of RadioToolButtons can be set, so that only one can be
selected at a time. When a button is clicked, it depresses and
is shaded darker.

It is also possible to set a tooltip to be dispalyed when the
user scrolls over it with their cursor as well as an accelerator
keyboard shortcut.
"""

import gi

gi.require_version("Gtk", "4.0")
gi.require_version("GObject", "2.0")

from gi.repository import Gtk, GObject
import logging

from sugar.graphics.toolbutton import ToolButton
from sugar.graphics import style

logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)


[docs] class RadioToolButton(ToolButton): """ A toolbar button that acts as a radio button. Radio tool buttons work in groups where only one button can be active at a time. When one button is activated, all others in the group are automatically deactivated. """ __gtype_name__ = "SugarRadioToolButton" __gsignals__ = {"toggled": (GObject.SignalFlags.RUN_FIRST, None, [])}
[docs] def __init__(self, group=None, **kwargs): """ Initialize the radio tool button. Args: group: Another RadioToolButton to group with, or None for new group **kwargs: Additional arguments passed to ToolButton """ super().__init__(**kwargs) self._group = [] self._active = False # Set up radio group if group is not None: self.set_group(group) else: self._group = [self] self.connect("clicked", self._on_clicked) # radio button styling self.add_css_class("radio-tool-button") self._apply_radio_styling()
def _apply_radio_styling(self): css = """ .radio-tool-button { border-radius: 4px; margin: 1px; } .radio-tool-button:checked, .radio-tool-button.active { background: alpha(@theme_selected_bg_color, 0.2); border: 1px solid @theme_selected_bg_color; } .radio-tool-button:hover:not(.active) { background: alpha(@theme_fg_color, 0.05); } """ style.apply_css_to_widget(self, css) def _on_clicked(self, button): """Handle button click.""" if not self._active: self.set_active(True)
[docs] def get_group(self): """Get the list of buttons in this radio group.""" return self._group[:]
[docs] def set_group(self, group_member): """ Set the radio group by specifying another member. Args: group_member: Another RadioToolButton to join groups with """ if group_member and isinstance(group_member, RadioToolButton): if self in self._group: self._group.remove(self) self._group = group_member._group if self not in self._group: self._group.append(self) for member in self._group: member._group = self._group
[docs] def get_active(self): """Get whether this button is active.""" return self._active
[docs] def set_active(self, active): """ Set the active state of this button. Args: active: True to activate, False to deactivate """ if active == self._active: return if active: # Deactivate all other buttons in group for member in self._group: if member != self and member._active: member._set_active_internal(False) self._set_active_internal(True) else: # Only allow deactivation if another button is being activated # or if this is the only button in the group if len(self._group) == 1: self._set_active_internal(False)
def _set_active_internal(self, active): """Internal method to set active state without group management.""" if active == self._active: return self._active = active if active: self.add_css_class("active") else: self.remove_css_class("active") self.emit("toggled") self.set_state_flags( Gtk.StateFlags.CHECKED if active else Gtk.StateFlags.NORMAL, True ) active = GObject.Property( type=bool, default=False, getter=get_active, setter=set_active, nick="Active", blurb="Whether the radio button is active", )