grx 0.3.2

Abstraction layer for UI development
Documentation
// SPDX-License-Identifier: GPL-3.0-or-later

//! A row element that can have title, subtitle, prefix and suffix children.
//! Should be placed in a `list`.
//!
//! # Child Annotations
//!
//! Children can be annotated via `#[prefix=true]` to place the child in the left area of the action_row.
//!
//! ```no_run
//! use grx::prelude::*;
//! grx! {
//!     action_row [
//!         #[prefix=true]
//!         button [
//!             "left"    
//!         ],
//!         button [
//!             "right"
//!         ]
//!     ]
//! };
//! ```

use std::{cell::Ref, rc::Rc};

use glib::prelude::Cast;
use grx_macros::gtk_component;
use gtk::glib;
use gtk::prelude::{ListBoxRowExt, WidgetExt};
use libadwaita::prelude::{ActionRowExt, PreferencesRowExt};

use crate::{annotations::Annotations, handlers::Handler, new_gc, props};

use super::{gtk_props::apply, utils::get_nth_child};

static ANNOTATIOIN_PREFIX: &str = "prefix";

#[props]
#[derive(Default)]
pub struct Props {
    pub title: String,
    pub subtitle: String,
    pub selectable: Option<bool>,
    pub activatable: Option<bool>,

    pub expand_suffix: Option<bool>,
    pub hide_title: Option<bool>,

    pub on_click: Option<Handler<ActionRow>>,
}

impl std::fmt::Debug for Props {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.debug_struct("Props")
            .field("title", &self.title)
            .field("subtitle", &self.subtitle)
            .field("selectable", &self.selectable)
            .field("activatable", &self.activatable)
            .field("expand_suffix", &self.expand_suffix)
            .field("hidke_title", &self.hide_title)
            .field("on_click", &self.on_click.is_some())
            .finish()
    }
}

pub fn action_row(mut props: Props) -> Rc<ActionRow> {
    let action_row = libadwaita::ActionRow::builder()
        .title(&props.title)
        .subtitle(&props.subtitle)
        .build();

    if let Some(b) = props.activatable {
        action_row.set_activatable(b)
    }
    if let Some(b) = props.selectable {
        action_row.set_selectable(b)
    }
    for c in &props.children {
        let inner = c.clone().inner();
        let w: &gtk::Widget = inner.downcast_ref().unwrap();
        let annotations: Ref<Annotations> = c.annotations();
        if let Some(true) = annotations
            .get(ANNOTATIOIN_PREFIX)
            .and_then(|v| v.as_bool())
        {
            action_row.add_prefix(w);
        } else {
            action_row.add_suffix(w);
        }
    }

    if let Some(true) = props.expand_suffix {
        if let Some(header) = get_nth_child::<gtk::Box>(&action_row, 0) {
            if let Some(suffixes) = get_nth_child::<gtk::Box>(&header, 3) {
                suffixes.set_hexpand(true);
            }
        }
    }

    if let Some(true) = props.hide_title {
        if let Some(header) = get_nth_child::<gtk::Box>(&action_row, 0) {
            if let Some(title) = get_nth_child::<gtk::Box>(&header, 2) {
                title.set_visible(false);
            }
        }
    }

    let click = props.on_click.take();

    let comp = new_gc!(ActionRow { action_row, props });

    if let Some(handler) = click {
        comp.on_click(handler);
    }

    apply(comp.clone());
    comp
}

#[gtk_component(libadwaita::ActionRow)]
#[derive(Debug)]
pub struct ActionRow {}

impl ActionRow {
    pub fn set_title(self: &Rc<Self>, title: &str) {
        self.widget.set_title(title);
    }
    pub fn set_subtitle(self: &Rc<Self>, subtitle: &str) {
        self.widget.set_subtitle(subtitle);
    }
    pub fn title(self: &Rc<Self>) -> String {
        self.widget.title().into()
    }
    pub fn subtitle(self: &Rc<Self>) -> Option<String> {
        self.widget.subtitle().map(|s| s.into())
    }
    pub fn on_click(self: &Rc<Self>, handler: Handler<Self>) {
        let s = self.clone();
        self.widget.connect_activated(move |_| {
            handler(s.clone());
        });
    }
}