fractal/session/view/content/room_history/message_row/
location.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
use adw::{prelude::*, subclass::prelude::*};
use geo_uri::GeoUri;
use gettextrs::gettext;
use gtk::{glib, CompositeTemplate};
use tracing::warn;

use super::ContentFormat;
use crate::components::LocationViewer;

mod imp {
    use glib::subclass::InitializingObject;

    use super::*;

    #[derive(Debug, Default, CompositeTemplate)]
    #[template(
        resource = "/org/gnome/Fractal/ui/session/view/content/room_history/message_row/location.ui"
    )]
    pub struct MessageLocation {
        #[template_child]
        overlay: TemplateChild<gtk::Overlay>,
        #[template_child]
        location: TemplateChild<LocationViewer>,
        #[template_child]
        overlay_error: TemplateChild<gtk::Image>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for MessageLocation {
        const NAME: &'static str = "ContentMessageLocation";
        type Type = super::MessageLocation;
        type ParentType = gtk::Widget;

        fn class_init(klass: &mut Self::Class) {
            Self::bind_template(klass);

            klass.set_accessible_role(gtk::AccessibleRole::Group);
        }

        fn instance_init(obj: &InitializingObject<Self>) {
            obj.init_template();
        }
    }

    impl ObjectImpl for MessageLocation {
        fn dispose(&self) {
            self.overlay.unparent();
        }
    }

    impl WidgetImpl for MessageLocation {
        fn measure(&self, orientation: gtk::Orientation, _for_size: i32) -> (i32, i32, i32, i32) {
            if self.location.compact() {
                if orientation == gtk::Orientation::Horizontal {
                    (75, 75, -1, -1)
                } else {
                    (50, 50, -1, -1)
                }
            } else {
                (300, 300, -1, -1)
            }
        }

        fn size_allocate(&self, width: i32, height: i32, baseline: i32) {
            let width = if self.location.compact() {
                width.min(75)
            } else {
                width
            };
            self.overlay
                .size_allocate(&gtk::Allocation::new(0, 0, width, height), baseline);
        }
    }

    impl MessageLocation {
        /// Set the `geo:` URI to display.
        pub(super) fn set_geo_uri(&self, uri: &str, format: ContentFormat) {
            let compact = matches!(format, ContentFormat::Compact | ContentFormat::Ellipsized);
            self.location.set_compact(compact);

            match GeoUri::parse(uri) {
                Ok(geo_uri) => {
                    self.location.set_location(&geo_uri);
                    self.overlay_error.set_visible(false);
                }
                Err(error) => {
                    warn!("Encountered invalid geo URI: {error}");
                    self.location.set_visible(false);
                    self.overlay_error.set_tooltip_text(Some(&gettext(
                        "Location is invalid and cannot be displayed",
                    )));
                    self.overlay_error.set_visible(true);
                }
            };

            let obj = self.obj();
            if compact {
                obj.set_halign(gtk::Align::Start);
                obj.add_css_class("compact");
            } else {
                obj.set_halign(gtk::Align::Fill);
                obj.remove_css_class("compact");
            }
        }
    }
}

glib::wrapper! {
    /// A widget displaying a location message in the timeline.
    pub struct MessageLocation(ObjectSubclass<imp::MessageLocation>)
        @extends gtk::Widget, @implements gtk::Accessible;
}

impl MessageLocation {
    /// Create a new location message.
    pub fn new() -> Self {
        glib::Object::new()
    }

    /// Set the `geo:` URI to display.
    pub(crate) fn set_geo_uri(&self, uri: &str, format: ContentFormat) {
        self.imp().set_geo_uri(uri, format);
    }
}