05_live_text_interaction/
05_live_text_interaction.rs1use std::hint::black_box;
2use std::path::PathBuf;
3
4use visionkit::prelude::*;
5
6fn asset_path() -> PathBuf {
7 PathBuf::from(env!("CARGO_MANIFEST_DIR"))
8 .join("examples")
9 .join("assets")
10 .join("live_text.png")
11}
12
13fn build_delegate() -> Result<LiveTextInteractionDelegate, Box<dyn std::error::Error>> {
14 let delegate = LiveTextInteractionDelegate::new()?;
15 delegate.set_should_begin(true)?;
16 delegate.set_should_handle_key_down_event(true)?;
17 delegate.set_should_show_menu_for_event(true)?;
18 delegate.set_contents_rect_override(Some(Rect::default()))?;
19
20 let content_view = LiveTextContentView::new()?;
21 content_view.set_frame(Rect {
22 x: 0.0,
23 y: 0.0,
24 width: 32.0,
25 height: 32.0,
26 })?;
27 delegate.set_content_view(Some(&content_view))?;
28
29 let updated_menu = LiveTextMenu {
30 title: "VisionKit".to_owned(),
31 items: vec![LiveTextMenuItem {
32 title: "Copy".to_owned(),
33 tag: LiveTextMenuTag::copy_image().map_or(0, LiveTextMenuTag::raw_value),
34 is_separator: false,
35 is_enabled: true,
36 is_hidden: false,
37 state: 0,
38 submenu: None,
39 }],
40 };
41 delegate.set_updated_menu(Some(&updated_menu))?;
42 Ok(delegate)
43}
44
45fn print_selection_state(
46 interaction: &LiveTextInteraction,
47) -> Result<(), Box<dyn std::error::Error>> {
48 match interaction.selected_ranges() {
49 Ok(ranges) => {
50 interaction.set_selected_ranges(&ranges)?;
51 println!("selected ranges: {}", ranges.len());
52 }
53 Err(error) => println!("selected ranges: {error}"),
54 }
55 match interaction.selected_attributed_text() {
56 Ok(text) => println!("selected attributed text runs: {}", text.runs.len()),
57 Err(error) => println!("selected attributed text runs: {error}"),
58 }
59 match interaction.supplementary_interface_font() {
60 Ok(font) => {
61 interaction.set_supplementary_interface_font(font.as_ref())?;
62 println!("supplementary font set: {}", font.is_some());
63 }
64 Err(error) => println!("supplementary font set: {error}"),
65 }
66 Ok(())
67}
68
69fn print_subject_state(
70 interaction: &LiveTextInteraction,
71) -> Result<(), Box<dyn std::error::Error>> {
72 println!(
73 "subject unavailable case: {:?}",
74 LiveTextSubjectUnavailable::ImageUnavailable
75 );
76 match interaction.begin_subject_analysis_if_necessary() {
77 Ok(()) => println!("subject analysis started"),
78 Err(error) => println!("subject analysis started: {error}"),
79 }
80 match interaction.subjects() {
81 Ok(subjects) => {
82 println!("subjects: {}", subjects.len());
83 println!(
84 "highlighted subjects: {}",
85 interaction.highlighted_subjects()?.len()
86 );
87 if let Some(subject) = subjects.first() {
88 println!("subject bounds: {:?}", subject.bounds()?);
89 }
90 match interaction.image_for_subjects(&subjects) {
91 Ok(image) => println!("subject image bytes: {}", image.png_data.len()),
92 Err(error) => println!("subject image bytes: {error}"),
93 }
94 }
95 Err(error) => println!("subjects: {error}"),
96 }
97 Ok(())
98}
99
100fn main() -> Result<(), Box<dyn std::error::Error>> {
101 if !ImageAnalyzer::is_supported() {
102 println!("ImageAnalyzer is not supported on this Mac");
103 return Ok(());
104 }
105
106 let asset_path = asset_path();
107 let analyzer = ImageAnalyzer::new()?;
108 let analysis = analyzer.analyze_image_at_path(
109 &asset_path,
110 ImageOrientation::Up,
111 &ImageAnalyzerConfiguration::new(ImageAnalysisTypes::TEXT),
112 )?;
113
114 let delegate = build_delegate()?;
115 let interaction = LiveTextInteraction::with_delegate(&delegate)?;
116 let tracking_view = LiveTextTrackingImageView::new()?;
117 tracking_view.set_image_at_path(&asset_path)?;
118 interaction.set_tracking_image_view(Some(&tracking_view))?;
119 interaction.track_image_at_path(&asset_path)?;
120 interaction.set_analysis(&analysis)?;
121 interaction.set_preferred_interaction_types(LiveTextInteractionTypes::AUTOMATIC_TEXT_ONLY)?;
122 interaction.set_selectable_items_highlighted(true)?;
123 interaction.set_contents_rect_needs_update()?;
124
125 println!("contents rect: {:?}", interaction.contents_rect()?);
126 println!("overlay text: {:?}", interaction.text());
127 println!("delegate events: {}", delegate.recorded_events()?.len());
128 println!(
129 "delegate content view set: {}",
130 interaction.delegate()?.and_then(|value| value.content_view().ok()).flatten().is_some()
131 );
132 match interaction.tracking_image_view()? {
133 Some(view) => println!("tracking image size: {:?}", view.image_size()?),
134 None => println!("tracking image size: none"),
135 }
136 match LiveTextMenuTag::copy_image() {
137 Ok(tag) => println!("copy image tag: {}", tag.raw_value()),
138 Err(error) => println!("copy image tag: {error}"),
139 }
140 print_selection_state(&interaction)?;
141 print_subject_state(&interaction)?;
142 println!(
143 "live text button visible: {}",
144 interaction.live_text_button_visible()?
145 );
146 println!(
147 "supplementary hidden: {}",
148 interaction.is_supplementary_interface_hidden()?
149 );
150
151 let image_for_subjects_fn: fn(&LiveTextInteraction, &[LiveTextSubject]) -> Result<
152 LiveTextImageData,
153 VisionKitError,
154 > = LiveTextInteraction::image_for_subjects;
155 black_box(image_for_subjects_fn);
156 Ok(())
157}