terminal_window.rs - icy_draw - [fork] icy_draw is the successor to mystic draw.
HTML git clone https://git.drkhsh.at/icy_draw.git
DIR Log
DIR Files
DIR Refs
DIR README
---
terminal_window.rs (31774B)
---
1 #![allow(clippy::float_cmp)]
2 use eframe::{
3 egui::{self, CursorIcon, PointerButton},
4 epaint::Vec2,
5 };
6 use egui::{ImageButton, Margin, Modifiers, RichText};
7 use i18n_embed_fl::fl;
8 use icy_engine::{Position, Selection, TextPane};
9 use web_time::Duration;
10
11 use crate::{
12 icons::{CALL, DOWNLOAD, KEY, LOGOUT, MENU, UPLOAD},
13 VERSION,
14 };
15
16 use super::{dialogs, MainWindow, MainWindowMode};
17
18 fn encode_mouse_button(button: i32) -> char {
19 unsafe { char::from_u32_unchecked(b' '.saturating_add(button as u8) as u32) }
20 }
21 fn encode_mouse_position(pos: i32) -> char {
22 unsafe { char::from_u32_unchecked(b'!'.saturating_add(pos as u8) as u32) }
23 }
24
25 impl MainWindow {
26 pub fn update_terminal_window(&mut self, ctx: &egui::Context, _frame: &mut eframe::Frame, show_dialing_directory: bool) {
27 let toolbar_bg_color = ctx.style().visuals.extreme_bg_color;
28 let button_frame = egui::containers::Frame::none().fill(toolbar_bg_color).inner_margin(Margin::same(6.0));
29
30 let enable_ui = matches!(self.get_mode(), MainWindowMode::ShowTerminal);
31
32 if !self.is_fullscreen_mode {
33 egui::TopBottomPanel::top("button_bar").frame(button_frame).show(ctx, |ui| {
34 if !enable_ui {
35 ui.disable();
36 }
37 ui.horizontal(|ui| {
38 let r = ui.add(ImageButton::new(UPLOAD.clone().tint(crate::ui::button_tint(ui)))).on_hover_ui(|ui| {
39 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "terminal-upload")).small());
40 });
41
42 if r.clicked() {
43 self.set_mode(ctx, MainWindowMode::SelectProtocol(false));
44 }
45
46 let r = ui.add(ImageButton::new(DOWNLOAD.clone().tint(crate::ui::button_tint(ui)))).on_hover_ui(|ui| {
47 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "terminal-download")).small());
48 });
49
50 if r.clicked() {
51 self.set_mode(ctx, MainWindowMode::SelectProtocol(true));
52 }
53 let mut send_login = false;
54 if let Some(auto_login) = &mut self.terminal_thread.lock().auto_login {
55 if !auto_login.logged_in {
56 let r = ui.add(ImageButton::new(KEY.clone().tint(crate::ui::button_tint(ui)))).on_hover_ui(|ui| {
57 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "terminal-autologin")).small());
58 });
59
60 if r.clicked() {
61 send_login = true;
62 auto_login.logged_in = true;
63 }
64 }
65 }
66 if send_login {
67 self.send_login();
68 }
69
70 let r: egui::Response = ui.add(ImageButton::new(CALL.clone().tint(crate::ui::button_tint(ui)))).on_hover_ui(|ui| {
71 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "terminal-dialing_directory")).small());
72 });
73
74 if r.clicked() {
75 self.set_mode(ctx, MainWindowMode::ShowDialingDirectory);
76 }
77
78 let mut mode = None;
79 if let Some(auto_login) = &mut self.terminal_thread.lock().auto_login {
80 if auto_login.iemsi.isi.is_some() {
81 if self.get_mode() == MainWindowMode::ShowIEMSI {
82 let r: egui::Response = ui.add(egui::Button::new(RichText::new(fl!(crate::LANGUAGE_LOADER, "toolbar-hide-iemsi"))));
83
84 if r.clicked() {
85 mode = Some(MainWindowMode::ShowTerminal);
86 }
87 } else {
88 let r: egui::Response = ui.add(egui::Button::new(RichText::new(fl!(crate::LANGUAGE_LOADER, "toolbar-show-iemsi"))));
89
90 if r.clicked() {
91 mode = Some(MainWindowMode::ShowIEMSI);
92 }
93 }
94 }
95 }
96
97 if let Some(mode) = mode {
98 self.set_mode(ctx, mode);
99 }
100
101 if self.terminal_thread.lock().sound_thread.lock().is_playing() {
102 let button_text = match self.terminal_thread.lock().sound_thread.lock().stop_button {
103 0 => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing1"),
104 1 => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing2"),
105 2 => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing3"),
106 3 => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing4"),
107 4 => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing5"),
108 _ => fl!(crate::LANGUAGE_LOADER, "toolbar-stop-playing6"),
109 };
110
111 let r: egui::Response = ui.add(egui::Button::new(RichText::new(button_text)));
112 if r.clicked() {
113 self.terminal_thread.lock().sound_thread.lock().clear();
114 }
115 }
116
117 if self.terminal_thread.lock().capture_dialog.capture_session {
118 let r: egui::Response = ui.add(egui::Button::new(RichText::new(fl!(crate::LANGUAGE_LOADER, "toolbar-stop-capture"))));
119
120 if r.clicked() {
121 self.terminal_thread.lock().capture_dialog.capture_session = false;
122 }
123 }
124
125 let size = ui.available_size_before_wrap();
126 ui.add_space(size.x - 70.0);
127
128 let r = ui.add(ImageButton::new(LOGOUT.clone().tint(crate::ui::button_tint(ui)))).on_hover_ui(|ui| {
129 ui.label(RichText::new(fl!(crate::LANGUAGE_LOADER, "terminal-hangup")).small());
130 });
131 if r.clicked() {
132 self.hangup(ctx);
133 }
134 ui.menu_image_button(MENU.clone().tint(crate::ui::button_tint(ui)), |ui| {
135 let r = ui.hyperlink_to(
136 fl!(crate::LANGUAGE_LOADER, "menu-item-discuss"),
137 "https://github.com/mkrueger/icy_tools/discussions",
138 );
139 if r.clicked() {
140 ui.close_menu();
141 }
142 let r = ui.hyperlink_to(
143 fl!(crate::LANGUAGE_LOADER, "menu-item-report-bug"),
144 "https://github.com/mkrueger/icy_tools/issues/new",
145 );
146 if r.clicked() {
147 ui.close_menu();
148 }
149 let r = ui.hyperlink_to(
150 fl!(crate::LANGUAGE_LOADER, "menu-item-check-releases"),
151 "https://github.com/mkrueger/icy_tools/releases",
152 );
153 if r.clicked() {
154 ui.close_menu();
155 }
156 ui.separator();
157 #[cfg(not(target_arch = "wasm32"))]
158 if ui.button(fl!(crate::LANGUAGE_LOADER, "menu-item-capture-dialog")).clicked() {
159 self.set_mode(ctx, MainWindowMode::ShowCaptureDialog);
160 ui.close_menu();
161 }
162
163 if ui.button(fl!(crate::LANGUAGE_LOADER, "menu-item-settings")).clicked() {
164 self.set_mode(ctx, MainWindowMode::ShowSettings);
165 ui.close_menu();
166 }
167 });
168 });
169 });
170 }
171 let frame_no_margins = egui::containers::Frame::none().outer_margin(Margin::same(0.0)).inner_margin(Margin::same(0.0));
172
173 egui::CentralPanel::default().frame(frame_no_margins).show(ctx, |ui| {
174 if !enable_ui {
175 ui.disable();
176 }
177 let rect = ui.available_rect_before_wrap();
178
179 self.show_terminal_area(ui);
180 let msg = if self.show_find_dialog { self.find_dialog.show_ui(ui, rect) } else { None };
181
182 match msg {
183 Some(dialogs::find_dialog::Message::ChangePattern(pattern)) => {
184 self.find_dialog.pattern = pattern.chars().collect();
185 let lock = &mut self.buffer_view.lock();
186 let (buffer, _, parser) = lock.get_edit_state_mut().get_buffer_and_caret_mut();
187 self.find_dialog.search_pattern(buffer, (*parser).as_ref());
188 self.find_dialog.update_pattern(lock);
189 }
190 Some(dialogs::find_dialog::Message::FindNext) => {
191 self.find_dialog.find_next(&mut self.buffer_view.lock());
192 }
193 Some(dialogs::find_dialog::Message::FindPrev) => {
194 self.find_dialog.find_prev(&mut self.buffer_view.lock());
195 }
196 Some(dialogs::find_dialog::Message::CloseDialog) => {
197 self.show_find_dialog = false;
198 }
199 Some(dialogs::find_dialog::Message::SetCasing(case_sensitive)) => {
200 self.find_dialog.case_sensitive = case_sensitive;
201 let lock = &mut self.buffer_view.lock();
202 let (buffer, _, parser) = lock.get_edit_state_mut().get_buffer_and_caret_mut();
203 self.find_dialog.search_pattern(buffer, (*parser).as_ref());
204 self.find_dialog.update_pattern(lock);
205 }
206
207 None => {}
208 }
209 });
210
211 if show_dialing_directory {
212 dialogs::dialing_directory_dialog::view_dialing_directory(self, ctx);
213 }
214
215 let take = self.terminal_thread.lock().auto_transfer.take();
216 if let Some((protocol_type, download, file_name)) = take {
217 self.initiate_file_transfer(ctx, protocol_type, download, file_name);
218 }
219
220 if self.terminal_thread_handle.is_some() && self.terminal_thread_handle.as_ref().unwrap().is_finished() {
221 let handle = self.terminal_thread_handle.take().unwrap();
222 if let Err(err) = handle.join() {
223 let err = format!("Error update thread crashed: {:?}", err.downcast_ref::<&str>());
224 log::error!("{err}");
225 self.output_string(&err);
226 }
227 }
228 ctx.request_repaint_after(Duration::from_millis(250));
229 }
230
231 fn show_terminal_area(&mut self, ui: &mut egui::Ui) {
232 let mut monitor_settings = self.get_options().monitor_settings.clone();
233
234 monitor_settings.selection_fg = self.screen_mode.get_selection_fg();
235 monitor_settings.selection_bg = self.screen_mode.get_selection_bg();
236 /* if ui.input(|i| i.key_down(egui::Key::W)) {
237 let enabled = self.buffer_update_thread.lock().enabled;
238 self.buffer_update_thread.lock().enabled = !enabled;
239 }*/
240
241 let opt = icy_engine_gui::TerminalOptions {
242 filter: self.get_options().scaling.get_filter(),
243 monitor_settings,
244 stick_to_bottom: true,
245 use_terminal_height: true,
246 ..Default::default()
247 };
248 let (mut response, calc) = icy_engine_gui::show_terminal_area(ui, self.buffer_view.clone(), opt);
249 let inner_response = response.context_menu(|ui| terminal_context_menu(ui, self));
250 if let Some(inner_response) = inner_response {
251 response = inner_response.response;
252 }
253
254 if matches!(self.get_mode(), MainWindowMode::ShowTerminal) && ui.is_enabled() && !self.show_find_dialog {
255 let events = ui.input(|i| i.events.clone());
256 for e in events {
257 match e {
258 egui::Event::PointerButton {
259 button: PointerButton::Middle,
260 pressed: true,
261 ..
262 } => {
263 self.copy_to_clipboard();
264 }
265 egui::Event::Paste(text) => {
266 self.output_string(&text);
267 }
268 /*egui::Event::CompositionEnd(text) |*/
269 egui::Event::Text(text) => {
270 for c in text.chars() {
271 self.output_char(c);
272 }
273 }
274
275 egui::Event::PointerButton {
276 pos,
277 button,
278 pressed: true,
279 modifiers,
280 } => {
281 if calc.buffer_rect.contains(pos - calc.terminal_rect.left_top().to_vec2()) && !calc.vert_scrollbar_rect.contains(pos) {
282 let buffer_view = self.buffer_view.clone();
283 let click_pos = calc.calc_click_pos(pos);
284 let mode: icy_engine::MouseMode = buffer_view.lock().get_buffer().terminal_state.mouse_mode;
285
286 match mode {
287 icy_engine::MouseMode::VT200 | icy_engine::MouseMode::VT200_Highlight => {
288 let mut modifier_mask = 0;
289 if matches!(button, PointerButton::Secondary) {
290 modifier_mask |= 1;
291 }
292 if modifiers.shift {
293 modifier_mask |= 4;
294 }
295 if modifiers.alt {
296 modifier_mask |= 8;
297 }
298 if modifiers.ctrl || modifiers.mac_cmd {
299 modifier_mask |= 16;
300 }
301 self.output_string(
302 format!(
303 "\x1b[M{}{}{}",
304 encode_mouse_button(modifier_mask),
305 encode_mouse_position(click_pos.x as i32),
306 encode_mouse_position(click_pos.y as i32 - calc.first_line as i32)
307 )
308 .as_str(),
309 );
310 }
311 icy_engine::MouseMode::X10 => {
312 self.output_string(
313 format!(
314 "\x1b[M{}{}{}",
315 encode_mouse_button(0),
316 encode_mouse_position(click_pos.x as i32),
317 encode_mouse_position(click_pos.y as i32)
318 )
319 .as_str(),
320 );
321 }
322 _ => {} /*
323 icy_engine::MouseMode::ButtonEvents => todo!(),
324 icy_engine::MouseMode::AnyEvents => todo!(),
325 icy_engine::MouseMode::FocusEvent => todo!(),
326 icy_engine::MouseMode::AlternateScroll => todo!(),
327 icy_engine::MouseMode::ExtendedMode => todo!(),
328 icy_engine::MouseMode::SGRExtendedMode => todo!(),
329 icy_engine::MouseMode::URXVTExtendedMode => todo!(),
330 icy_engine::MouseMode::PixelPosition => todo!(),*/
331 }
332 }
333 }
334 egui::Event::PointerButton {
335 pos,
336 button: PointerButton::Primary,
337 pressed: false,
338 modifiers,
339 ..
340 } => {
341 if calc.buffer_rect.contains(pos - calc.terminal_rect.left_top().to_vec2()) && !calc.vert_scrollbar_rect.contains(pos) {
342 let mode: icy_engine::MouseMode = self.buffer_view.lock().get_buffer().terminal_state.mouse_mode;
343 match mode {
344 icy_engine::MouseMode::VT200 | icy_engine::MouseMode::VT200_Highlight => {
345 if calc.buffer_rect.contains(pos) {
346 let click_pos = calc.calc_click_pos(pos);
347 let mut modifier_mask = 3; // 3 means realease
348 if modifiers.shift {
349 modifier_mask |= 4;
350 }
351 if modifiers.alt {
352 modifier_mask |= 8;
353 }
354 if modifiers.ctrl || modifiers.mac_cmd {
355 modifier_mask |= 16;
356 }
357 self.output_string(
358 format!(
359 "\x1b[M{}{}{}",
360 encode_mouse_button(modifier_mask),
361 encode_mouse_position(click_pos.x as i32),
362 encode_mouse_position(click_pos.y as i32)
363 )
364 .as_str(),
365 );
366 }
367 }
368 _ => {}
369 }
370 }
371 }
372
373 egui::Event::PointerMoved(pos) => {
374 if calc.buffer_rect.contains(pos - calc.terminal_rect.left_top().to_vec2()) && !calc.vert_scrollbar_rect.contains(pos) {
375 // Dev feature in debug mode - print char under cursor
376 // when shift is pressed
377 if cfg!(debug_assertions) && ui.input(|i| i.modifiers.shift_only()) {
378 let click_pos: Vec2 = calc.calc_click_pos(pos);
379 let buffer_view = self.buffer_view.clone();
380
381 let ch = buffer_view.lock().get_buffer().get_char((click_pos.x as usize, click_pos.y as usize));
382 println!("Char under cursor: {ch:?}");
383 }
384 }
385 }
386
387 egui::Event::Cut => {
388 self.handle_key_press(ui, &response, egui::Key::X, Modifiers::CTRL);
389 }
390 egui::Event::Copy => {
391 self.handle_key_press(ui, &response, egui::Key::C, Modifiers::CTRL);
392 }
393 egui::Event::Key {
394 key, pressed: true, modifiers, ..
395 } => {
396 self.handle_key_press(ui, &response, key, modifiers);
397 }
398 _ => {}
399 }
400 }
401 if self.use_rip {
402 if response.clicked_by(PointerButton::Primary) {
403 if let Some(mouse_pos) = response.hover_pos() {
404 let mouse_pos = mouse_pos.to_vec2() - calc.buffer_rect.left_top().to_vec2();
405
406 let x = (mouse_pos.x / calc.buffer_rect.width() * 640.0) as i32;
407 let y = (mouse_pos.y / calc.buffer_rect.height() * 350.0) as i32;
408 let mut found_field = None;
409 for mouse_field in &self.terminal_thread.lock().mouse_field {
410 if !mouse_field.style.is_mouse_button() {
411 continue;
412 }
413 if mouse_field.contains(x, y) {
414 if let Some(found_field) = &found_field {
415 if mouse_field.contains_field(found_field) {
416 continue;
417 }
418 }
419 found_field = Some(mouse_field.clone());
420 }
421 }
422
423 if let Some(mouse_field) = &found_field {
424 if let Some(cmd) = &mouse_field.host_command {
425 if mouse_field.style.reset_screen_after_click() {
426 let mut buffer = self.buffer_view.lock();
427 buffer.get_buffer_mut().terminal_state.clear_margins_left_right();
428 buffer.get_buffer_mut().terminal_state.clear_margins_top_bottom();
429 buffer.clear_buffer_screen();
430 }
431 self.output_string(cmd);
432 return;
433 }
434 }
435 }
436 }
437
438 if response.hovered() {
439 let hover_pos_opt = ui.input(|i| i.pointer.hover_pos());
440 if let Some(hover_pos) = hover_pos_opt {
441 let hover_pos = hover_pos.to_vec2() - calc.buffer_rect.left_top().to_vec2();
442
443 let x = (hover_pos.x / calc.buffer_rect.width() * 640.0) as i32;
444 let y = (hover_pos.y / calc.buffer_rect.height() * 350.0) as i32;
445 let fields = &self.terminal_thread.lock().mouse_field;
446 for mouse_field in fields {
447 if !mouse_field.style.is_mouse_button() {
448 continue;
449 }
450 if mouse_field.contains(x, y) {
451 ui.output_mut(|o: &mut egui::PlatformOutput| o.cursor_icon = CursorIcon::PointingHand);
452 break;
453 }
454 }
455 }
456 }
457 return;
458 }
459
460 if response.clicked_by(PointerButton::Primary) {
461 if let Some(mouse_pos) = response.hover_pos() {
462 if calc.buffer_rect.contains(mouse_pos) && !calc.vert_scrollbar_rect.contains(mouse_pos) {
463 self.buffer_view.lock().clear_selection();
464 }
465 }
466 }
467
468 if response.drag_started_by(PointerButton::Primary) {
469 self.drag_start = None;
470 if let Some(mouse_pos) = response.hover_pos() {
471 if calc.buffer_rect.contains(mouse_pos) && !calc.vert_scrollbar_rect.contains(mouse_pos) {
472 let click_pos = calc.calc_click_pos(mouse_pos);
473 self.last_pos = Position::new(click_pos.x as i32, click_pos.y as i32);
474 self.drag_start = Some(click_pos);
475 self.buffer_view.lock().get_edit_state_mut().set_mask_size();
476 self.buffer_view.lock().set_selection(Selection::new((click_pos.x, click_pos.y)));
477 self.buffer_view.lock().get_selection().as_mut().unwrap().shape = if response.ctx.input(|i| i.modifiers.alt) {
478 icy_engine::Shape::Rectangle
479 } else {
480 icy_engine::Shape::Lines
481 };
482 }
483 }
484 self.last_pos = Position::new(-1, -1);
485 }
486
487 if response.dragged_by(PointerButton::Primary) && self.drag_start.is_some() {
488 if let Some(mouse_pos) = response.hover_pos() {
489 let click_pos = calc.calc_click_pos(mouse_pos);
490 let cur = Position::new(click_pos.x as i32, click_pos.y as i32);
491
492 if cur != self.last_pos {
493 self.last_pos = cur;
494 let mut l = self.buffer_view.lock();
495 l.get_edit_state_mut().set_mask_size();
496
497 if let Some(sel) = &mut l.get_selection() {
498 if !sel.locked {
499 sel.lead = Position::new(click_pos.x as i32, click_pos.y as i32);
500 sel.shape = if ui.input(|i| i.modifiers.alt) {
501 icy_engine::Shape::Rectangle
502 } else {
503 icy_engine::Shape::Lines
504 };
505 l.clear_selection();
506 l.set_selection(*sel);
507 let _ = l.get_edit_state_mut().add_selection_to_mask();
508 l.redraw_view();
509 }
510 }
511 }
512 }
513 }
514
515 if response.drag_stopped_by(PointerButton::Primary) && self.drag_start.is_some() {
516 self.shift_pressed_during_selection = ui.input(|i| i.modifiers.shift);
517 if response.hover_pos().is_some() {
518 let l = self.buffer_view.lock();
519 if let Some(sel) = &mut l.get_selection() {
520 sel.locked = true;
521 }
522 }
523 self.last_pos = Position::new(-1, -1);
524
525 self.drag_start = None;
526 }
527
528 if response.hovered() {
529 let hover_pos_opt = ui.input(|i| i.pointer.hover_pos());
530 if let Some(hover_pos) = hover_pos_opt {
531 if calc.buffer_rect.contains(hover_pos) {
532 let click_pos = calc.calc_click_pos(hover_pos);
533 let mut hovered_link = false;
534 let lock = self.buffer_view.lock();
535 let buffer = lock.get_buffer();
536 for hyper_link in buffer.layers[0].hyperlinks() {
537 if buffer.is_position_in_range(Position::new(click_pos.x as i32, click_pos.y as i32), hyper_link.position, hyper_link.length) {
538 ui.output_mut(|o: &mut egui::PlatformOutput| o.cursor_icon = CursorIcon::PointingHand);
539 let url = hyper_link.get_url(buffer);
540 response = response.on_hover_ui_at_pointer(|ui| {
541 ui.hyperlink(url.clone());
542 });
543 hovered_link = true;
544
545 if response.clicked_by(PointerButton::Primary)
546 /* && response.is_pointer_button_down_on() */
547 {
548 ui.ctx().output_mut(|o| {
549 o.open_url = Some(egui::output::OpenUrl { url, new_tab: false });
550 });
551 }
552 break;
553 }
554 }
555 if !hovered_link && !calc.vert_scrollbar_rect.contains(hover_pos) {
556 ui.output_mut(|o| o.cursor_icon = CursorIcon::Text);
557 }
558 }
559 }
560 }
561 }
562 }
563
564 fn handle_key_press(&mut self, ui: &mut egui::Ui, response: &egui::Response, key: egui::Key, modifiers: egui::Modifiers) {
565 let im = self.screen_mode.get_input_mode();
566 let key_map = im.cur_map();
567 let mut key_code = key as u32;
568 if modifiers.ctrl || modifiers.command {
569 key_code |= icy_engine_gui::ui::CTRL_MOD;
570 }
571 if modifiers.shift {
572 key_code |= icy_engine_gui::ui::SHIFT_MOD;
573 }
574 for (k, m) in key_map {
575 if *k == key_code {
576 if self.terminal_thread.lock().is_connected {
577 self.send_vec(m.to_vec());
578 } else {
579 for c in *m {
580 self.print_char(*c);
581 }
582 }
583 response.request_focus();
584
585 ui.input_mut(|i| i.consume_key(modifiers, key));
586 break;
587 }
588 }
589 }
590
591 fn copy_to_clipboard(&mut self) {
592 let buffer_view = self.buffer_view.clone();
593 let mut l = buffer_view.lock();
594 if self.shift_pressed_during_selection {
595 if let Some(data) = l.get_edit_state().get_clipboard_data() {
596 if let Err(err) = icy_engine::util::push_data(icy_engine::util::BUFFER_DATA, &data) {
597 log::error!("error while copy:{err}");
598 }
599 return;
600 }
601 }
602
603 if let Some(txt) = l.get_copy_text() {
604 let mut clipboard = arboard::Clipboard::new().unwrap();
605 clipboard.set_text(txt).unwrap();
606 }
607 l.clear_selection();
608 }
609 }
610
611 fn terminal_context_menu(ui: &mut egui::Ui, window: &mut MainWindow) {
612 ui.input_mut(|i| i.events.clear());
613
614 if ui.button(fl!(crate::LANGUAGE_LOADER, "terminal-menu-copy")).clicked() {
615 window.copy_to_clipboard();
616 ui.close_menu();
617 }
618
619 if ui.button(fl!(crate::LANGUAGE_LOADER, "terminal-menu-paste")).clicked() {
620 let mut clipboard = arboard::Clipboard::new().unwrap();
621 if let Ok(text) = clipboard.get_text() {
622 let im = window.screen_mode.get_input_mode();
623 let key_map = im.cur_map();
624 let mut first = true;
625 let mut txt = String::new();
626 text.lines().for_each(|line| {
627 if first {
628 first = false;
629 } else {
630 for (k, m) in key_map {
631 if *k == eframe::egui::Key::Enter as u32 {
632 for c in *m {
633 txt.push(*c as char);
634 }
635 }
636 }
637 }
638 txt.push_str(line);
639 });
640 window.output_string(&txt);
641 }
642 ui.close_menu();
643 }
644
645 #[cfg(not(target_arch = "wasm32"))]
646 {
647 ui.separator();
648 if ui
649 .add(egui::Button::new(fl!(crate::LANGUAGE_LOADER, "terminal-menu-export")).wrap_mode(egui::TextWrapMode::Truncate))
650 .clicked()
651 {
652 window.init_export_dialog(ui.ctx());
653 ui.close_menu();
654 }
655 }
656 }