use core::convert::TryInto;
use glib::object::IsA;
use gtk4::prelude::*;
use crate::GenerateRoutingGtkHandler;
#[derive(Clone)]
pub struct BuilderFactory {
xml: String,
signals: Vec<String>,
}
fn extract_signals(xml: &str) -> Vec<String> {
use quick_xml::events::Event;
use quick_xml::Reader;
let mut reader = Reader::from_str(xml);
let mut buf = Vec::new();
let mut result = Vec::new();
loop {
match reader.read_event_into(&mut buf).unwrap() {
Event::Eof => {
break;
}
Event::Empty(tag) if tag.name().0 == b"signal" => {
if let Some(handler) = tag.try_get_attribute("handler").unwrap() {
result.push(String::from_utf8(handler.value.to_vec()).unwrap());
}
}
_ => {}
}
}
result
}
impl From<String> for BuilderFactory {
fn from(xml: String) -> Self {
let signals = extract_signals(&xml);
Self { xml, signals }
}
}
impl BuilderFactory {
pub fn instantiate_without_routing_signals(&self) -> BuilderWidgets {
gtk4::Builder::from_string(&self.xml).into()
}
pub fn instantiate_with_scope(&self, scope: &impl IsA<gtk4::BuilderScope>) -> BuilderWidgets {
let builder = gtk4::Builder::new();
builder.set_scope(Some(scope));
builder.add_from_string(&self.xml).unwrap();
builder.into()
}
pub fn instantiate_route_to(&self, target: impl crate::IntoGenerateRoutingGtkHandler) -> BuilderWidgets {
let scope = gtk4::BuilderRustScope::new();
let generator = target.into_generate_routing_gtk_handler();
for signal_name in self.signals.iter() {
generator.register_into_builder_rust_scope(&scope, signal_name);
}
self.instantiate_with_scope(&scope)
}
}
pub struct BuilderWidgets {
pub builder: gtk4::Builder,
}
impl From<gtk4::Builder> for BuilderWidgets {
fn from(builder: gtk4::Builder) -> Self {
Self { builder }
}
}
impl BuilderWidgets {
pub fn set_application(&self, app: &impl IsA<gtk4::Application>) {
for object in self.builder.objects() {
if let Some(window) = object.downcast_ref::<gtk4::Window>() {
window.set_application(Some(app));
}
}
}
pub fn get_object<W>(&self, id: &str) -> Result<W, crate::Error>
where
W: IsA<glib::Object>,
{
self.builder.object::<W>(id).ok_or_else(|| {
if let Some(object) = self.builder.object::<glib::Object>(id) {
crate::Error::IncorrectWidgetTypeInBuilder {
widget_id: id.to_owned(),
expected_type: <W as glib::types::StaticType>::static_type(),
actual_type: object.type_(),
}
} else {
crate::Error::WidgetMissingInBuilder(id.to_owned())
}
})
}
pub fn with_object<W>(&self, id: &str, dlg: impl FnOnce(W)) -> &Self
where
W: IsA<glib::Object>,
{
dlg(self.get_object(id).unwrap());
self
}
pub fn widgets<W>(&self) -> Result<W, <gtk4::Builder as TryInto<W>>::Error>
where
gtk4::Builder: TryInto<W>,
{
self.builder.clone().try_into()
}
}