use adw::subclass::prelude::*;
use gtk::{glib, pango, prelude::*};
use super::LoadingBin;
mod imp {
use std::marker::PhantomData;
use super::*;
#[derive(Debug, Default, glib::Properties)]
#[properties(wrapper_type = super::LoadingButton)]
pub struct LoadingButton {
pub loading_bin: LoadingBin,
#[property(get = Self::content_label, set = Self::set_content_label, explicit_notify)]
pub content_label: PhantomData<Option<glib::GString>>,
#[property(get = Self::content_icon_name, set = Self::set_content_icon_name, explicit_notify)]
pub content_icon_name: PhantomData<Option<glib::GString>>,
#[property(get = Self::is_loading, set = Self::set_is_loading, explicit_notify)]
pub is_loading: PhantomData<bool>,
}
#[glib::object_subclass]
impl ObjectSubclass for LoadingButton {
const NAME: &'static str = "LoadingButton";
type Type = super::LoadingButton;
type ParentType = gtk::Button;
}
#[glib::derived_properties]
impl ObjectImpl for LoadingButton {
fn constructed(&self) {
self.parent_constructed();
self.obj().set_child(Some(&self.loading_bin));
}
}
impl WidgetImpl for LoadingButton {}
impl ButtonImpl for LoadingButton {}
impl LoadingButton {
fn content_label(&self) -> Option<glib::GString> {
self.loading_bin
.child()
.and_downcast::<gtk::Label>()
.map(|l| l.label())
.filter(|s| !s.is_empty())
}
fn set_content_label(&self, label: &str) {
if self.content_label().as_deref() == Some(label) {
return;
}
let obj = self.obj();
let child_label =
if let Some(child_label) = self.loading_bin.child().and_downcast::<gtk::Label>() {
child_label
} else {
let child_label = gtk::Label::builder()
.ellipsize(pango::EllipsizeMode::End)
.use_underline(true)
.mnemonic_widget(&*obj)
.css_classes(["text-button"])
.build();
self.loading_bin.set_child(Some(child_label.clone()));
obj.remove_css_class("image-button");
obj.update_relation(&[gtk::accessible::Relation::LabelledBy(&[
child_label.upcast_ref()
])]);
child_label
};
child_label.set_label(label);
obj.notify_content_label();
}
fn content_icon_name(&self) -> Option<glib::GString> {
self.loading_bin
.child()
.and_downcast::<gtk::Image>()
.and_then(|i| i.icon_name())
}
fn set_content_icon_name(&self, icon_name: &str) {
if self.content_icon_name().as_deref() == Some(icon_name) {
return;
}
let obj = self.obj();
let child_image =
if let Some(child_image) = self.loading_bin.child().and_downcast::<gtk::Image>() {
child_image
} else {
let child_image = gtk::Image::builder()
.accessible_role(gtk::AccessibleRole::Presentation)
.build();
self.loading_bin.set_child(Some(child_image.clone()));
obj.add_css_class("image-button");
child_image
};
child_image.set_icon_name(Some(icon_name));
obj.notify_content_icon_name();
}
fn is_loading(&self) -> bool {
self.loading_bin.is_loading()
}
fn set_is_loading(&self, is_loading: bool) {
if self.is_loading() == is_loading {
return;
}
let obj = self.obj();
if obj.action_name().is_none() {
obj.set_sensitive(!is_loading);
}
self.loading_bin.set_is_loading(is_loading);
obj.notify_is_loading();
}
}
}
glib::wrapper! {
pub struct LoadingButton(ObjectSubclass<imp::LoadingButton>)
@extends gtk::Widget, gtk::Button, @implements gtk::Accessible, gtk::Actionable;
}
impl LoadingButton {
pub fn new() -> Self {
glib::Object::new()
}
}
impl Default for LoadingButton {
fn default() -> Self {
Self::new()
}
}