rotulus 0.2.2

A GTK4 scrollback view for text-stream chat
Documentation
//! Rotulus — a GTK4 scrollback view for text-stream chat.
//!
//! The GTK skin over [`rotulus_layout`]. All the layout logic lives in
//! that crate and is tested headless; this one owns the widget, the
//! Pango font backend, and the C ABI declared in `include/rotulus.h`.
//!
//! What it promises, and what it doesn't, is in docs/design.md.
//!
//! # Translations
//!
//! The widget's few strings (its context menus) are looked up in the
//! `rotulus` gettext domain. An application that ships the catalog binds
//! it the usual way, `bindtextdomain("rotulus", localedir)`; one that
//! doesn't gets English.

mod a11y;
mod contrast;
pub mod ffi;
pub mod links;
pub mod measure;
pub mod message_ffi;
#[cfg(feature = "mirc")]
pub mod mirc_ffi;
pub mod view;

/// The gettext domain the widget's own strings live in.
pub const GETTEXT_DOMAIN: &str = "rotulus";

/// Translate `s` in the widget's own domain, falling back to the msgid
/// on any failure.
pub(crate) fn tr(s: &str) -> String {
    use std::ffi::{c_char, CStr, CString};
    extern "C" {
        fn dgettext(domain: *const c_char, msgid: *const c_char) -> *mut c_char;
    }
    let (Ok(domain), Ok(c)) = (CString::new(GETTEXT_DOMAIN), CString::new(s)) else {
        return s.to_owned();
    };
    unsafe {
        let p = dgettext(domain.as_ptr(), c.as_ptr());
        if p.is_null() {
            return s.to_owned();
        }
        CStr::from_ptr(p).to_string_lossy().into_owned()
    }
}

/// Translate `s` in `context`, as `pgettext` does: for a short string
/// that could mean several things.
pub(crate) fn trc(context: &str, s: &str) -> String {
    let key = format!("{context}\u{4}{s}");
    let t = tr(&key);
    if t == key {
        s.to_owned()
    } else {
        t
    }
}

/// Tell gtk4-rs that GTK is already initialized, when it is.
///
/// An application written in C calls `gtk_init` itself, so gtk4-rs's own
/// init flag is never set — and its widget constructors assert on that
/// flag, aborting across the FFI even though GTK is running. Every C-ABI
/// entry point that constructs a widget calls this first.
///
/// Registering the type doesn't need GTK, and the introspection scanner
/// asks for it without ever initializing GTK; gtk4-rs asserts that GTK
/// really is up, so only say so when it is.
pub(crate) fn ensure_gtk_init() {
    if gtk4::is_initialized() {
        return;
    }
    if unsafe { gtk4::ffi::gtk_is_initialized() } != 0 {
        unsafe { gtk4::set_initialized() };
    }
}

#[cfg(test)]
mod tests;

pub use measure::PangoMeasure;
pub use view::{AvatarFunc, LoadDirection, RotulusView};