fractal/session/view/
event_details_dialog.rs1use adw::{prelude::*, subclass::prelude::*};
2use gettextrs::gettext;
3use gtk::{glib, CompositeTemplate};
4use sourceview::prelude::*;
5
6use crate::{
7 components::{CopyableRow, ToastableDialog},
8 prelude::*,
9 session::model::Event,
10 toast, utils,
11 utils::template_callbacks::TemplateCallbacks,
12};
13
14mod imp {
15 use std::cell::RefCell;
16
17 use glib::subclass::InitializingObject;
18
19 use super::*;
20
21 #[derive(Debug, Default, CompositeTemplate, glib::Properties)]
22 #[template(resource = "/org/gnome/Fractal/ui/session/view/event_details_dialog.ui")]
23 #[properties(wrapper_type = super::EventDetailsDialog)]
24 pub struct EventDetailsDialog {
25 #[property(get, construct_only)]
27 event: RefCell<Option<Event>>,
28 #[template_child]
29 navigation_view: TemplateChild<adw::NavigationView>,
30 #[template_child]
31 source_page: TemplateChild<adw::NavigationPage>,
32 #[template_child]
33 source_view: TemplateChild<sourceview::View>,
34 }
35
36 #[glib::object_subclass]
37 impl ObjectSubclass for EventDetailsDialog {
38 const NAME: &'static str = "EventDetailsDialog";
39 type Type = super::EventDetailsDialog;
40 type ParentType = ToastableDialog;
41
42 fn class_init(klass: &mut Self::Class) {
43 CopyableRow::ensure_type();
44
45 Self::bind_template(klass);
46 Self::bind_template_callbacks(klass);
47 TemplateCallbacks::bind_template_callbacks(klass);
48 }
49
50 fn instance_init(obj: &InitializingObject<Self>) {
51 obj.init_template();
52 }
53 }
54
55 #[glib::derived_properties]
56 impl ObjectImpl for EventDetailsDialog {
57 fn constructed(&self) {
58 self.parent_constructed();
59
60 let json_lang = sourceview::LanguageManager::default().language("json");
61
62 let buffer = self
63 .source_view
64 .buffer()
65 .downcast::<sourceview::Buffer>()
66 .unwrap();
67 buffer.set_language(json_lang.as_ref());
68 utils::sourceview::setup_style_scheme(&buffer);
69 }
70 }
71
72 impl WidgetImpl for EventDetailsDialog {}
73 impl AdwDialogImpl for EventDetailsDialog {}
74 impl ToastableDialogImpl for EventDetailsDialog {}
75
76 #[gtk::template_callbacks]
77 impl EventDetailsDialog {
78 fn show_source(&self, title: &str, source: &str) {
80 self.source_view.buffer().set_text(source);
81 self.source_page.set_title(title);
82 self.navigation_view.push_by_tag("source");
83 }
84
85 #[template_callback]
87 fn show_original_source(&self) {
88 let Some(event) = self.event.borrow().clone() else {
89 return;
90 };
91
92 if let Some(source) = event.source() {
93 let title = if event.is_edited() {
94 gettext("Original Event Source")
95 } else {
96 gettext("Event Source")
97 };
98 self.show_source(&title, &source);
99 }
100 }
101
102 #[template_callback]
104 fn show_edit_source(&self) {
105 let Some(event) = self.event.borrow().clone() else {
106 return;
107 };
108
109 let source = event.latest_edit_source();
110 let title = gettext("Latest Edit Source");
111 self.show_source(&title, &source);
112 }
113
114 #[template_callback]
116 fn copy_source(&self) {
117 let obj = self.obj();
118
119 let buffer = self.source_view.buffer();
120 let (start_iter, end_iter) = buffer.bounds();
121 obj.clipboard()
122 .set_text(&buffer.text(&start_iter, &end_iter, true));
123
124 toast!(obj, gettext("Source copied to clipboard"));
125 }
126 }
127}
128
129glib::wrapper! {
130 pub struct EventDetailsDialog(ObjectSubclass<imp::EventDetailsDialog>)
132 @extends gtk::Widget, adw::Dialog, ToastableDialog, @implements gtk::Accessible;
133}
134
135impl EventDetailsDialog {
136 pub fn new(event: &Event) -> Self {
137 glib::Object::builder().property("event", event).build()
138 }
139}