fractal/session/view/
event_details_dialog.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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
use adw::{prelude::*, subclass::prelude::*};
use gettextrs::gettext;
use gtk::{glib, CompositeTemplate};
use sourceview::prelude::*;

use crate::{
    components::{CopyableRow, ToastableDialog},
    prelude::*,
    session::model::Event,
    toast, utils,
    utils::template_callbacks::TemplateCallbacks,
};

mod imp {
    use std::cell::RefCell;

    use glib::subclass::InitializingObject;

    use super::*;

    #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
    #[template(resource = "/org/gnome/Fractal/ui/session/view/event_details_dialog.ui")]
    #[properties(wrapper_type = super::EventDetailsDialog)]
    pub struct EventDetailsDialog {
        /// The event that is displayed in the dialog.
        #[property(get, construct_only)]
        event: RefCell<Option<Event>>,
        #[template_child]
        navigation_view: TemplateChild<adw::NavigationView>,
        #[template_child]
        source_page: TemplateChild<adw::NavigationPage>,
        #[template_child]
        source_view: TemplateChild<sourceview::View>,
    }

    #[glib::object_subclass]
    impl ObjectSubclass for EventDetailsDialog {
        const NAME: &'static str = "EventDetailsDialog";
        type Type = super::EventDetailsDialog;
        type ParentType = ToastableDialog;

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

            Self::bind_template(klass);
            Self::bind_template_callbacks(klass);
            TemplateCallbacks::bind_template_callbacks(klass);
        }

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

    #[glib::derived_properties]
    impl ObjectImpl for EventDetailsDialog {
        fn constructed(&self) {
            self.parent_constructed();

            let json_lang = sourceview::LanguageManager::default().language("json");

            let buffer = self
                .source_view
                .buffer()
                .downcast::<sourceview::Buffer>()
                .unwrap();
            buffer.set_language(json_lang.as_ref());
            utils::sourceview::setup_style_scheme(&buffer);
        }
    }

    impl WidgetImpl for EventDetailsDialog {}
    impl AdwDialogImpl for EventDetailsDialog {}
    impl ToastableDialogImpl for EventDetailsDialog {}

    #[gtk::template_callbacks]
    impl EventDetailsDialog {
        /// View the given source.
        fn show_source(&self, title: &str, source: &str) {
            self.source_view.buffer().set_text(source);
            self.source_page.set_title(title);
            self.navigation_view.push_by_tag("source");
        }

        /// View the original source.
        #[template_callback]
        fn show_original_source(&self) {
            let Some(event) = self.event.borrow().clone() else {
                return;
            };

            if let Some(source) = event.source() {
                let title = if event.is_edited() {
                    gettext("Original Event Source")
                } else {
                    gettext("Event Source")
                };
                self.show_source(&title, &source);
            }
        }

        /// View the source of the latest edit.
        #[template_callback]
        fn show_edit_source(&self) {
            let Some(event) = self.event.borrow().clone() else {
                return;
            };

            let source = event.latest_edit_source();
            let title = gettext("Latest Edit Source");
            self.show_source(&title, &source);
        }

        /// Copy the source that is currently shown.
        #[template_callback]
        fn copy_source(&self) {
            let obj = self.obj();

            let buffer = self.source_view.buffer();
            let (start_iter, end_iter) = buffer.bounds();
            obj.clipboard()
                .set_text(&buffer.text(&start_iter, &end_iter, true));

            toast!(obj, gettext("Source copied to clipboard"));
        }
    }
}

glib::wrapper! {
    /// A dialog showing the details of an event.
    pub struct EventDetailsDialog(ObjectSubclass<imp::EventDetailsDialog>)
        @extends gtk::Widget, adw::Dialog, ToastableDialog, @implements gtk::Accessible;
}

impl EventDetailsDialog {
    pub fn new(event: &Event) -> Self {
        glib::Object::builder().property("event", event).build()
    }
}