app.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
---
app.rs (10260B)
---
1 #![allow(unsafe_code, clippy::wildcard_imports)]
2
3 use std::{path::PathBuf, sync::Arc, time::Duration};
4
5 use directories::UserDirs;
6 use eframe::egui::{self};
7 use egui::{mutex::Mutex, FontId, Rect};
8 use icy_engine::Position;
9 use icy_net::{
10 protocol::TransferState,
11 telnet::{TermCaps, TerminalEmulation},
12 ConnectionType,
13 };
14 use web_time::Instant;
15
16 use crate::{
17 features::AutoFileTransfer,
18 get_unicode_converter,
19 ui::{connect::OpenConnectionData, dialogs, terminal_thread::TerminalThread, BufferView, MainWindowState, ScreenMode},
20 util::SoundThread,
21 AddressBook, Options,
22 };
23
24 use super::{MainWindow, MainWindowMode};
25
26 impl MainWindow {
27 pub fn new(cc: &eframe::CreationContext<'_>, options: Options) -> Self {
28 use egui::FontFamily::Proportional;
29 use egui::TextStyle::{Body, Button, Heading, Monospace, Small};
30 egui_extras::install_image_loaders(&cc.egui_ctx);
31
32 let gl = cc.gl.as_ref().expect("You need to run eframe with the glow backend");
33 let mut view = BufferView::new(gl);
34 view.interactive = true;
35 view.get_edit_state_mut().set_unicode_converter(get_unicode_converter(&TerminalEmulation::Ansi));
36
37 let addresses: AddressBook = match crate::addresses::start_read_book() {
38 Ok(addresses) => addresses,
39 Err(e) => {
40 log::error!("Error reading dialing_directory: {e}");
41 AddressBook::default()
42 }
43 };
44
45 // #[cfg(not(target_arch = "wasm32"))]
46 // let is_fullscreen_mode = cc.integration_info.window_info.fullscreen;
47 // #[cfg(target_arch = "wasm32")]
48 let is_fullscreen_mode = false;
49
50 // try to detect dark vs light mode from the host system; default to dark
51 let is_dark = if let Some(dark_mode) = &options.is_dark_mode {
52 *dark_mode
53 } else {
54 dark_light::detect() != dark_light::Mode::Light
55 };
56 let ctx: &egui::Context = &cc.egui_ctx;
57 ctx.set_visuals(if is_dark { egui::Visuals::dark() } else { egui::Visuals::light() });
58
59 let mut initial_upload_directory = None;
60
61 if let Some(dirs) = UserDirs::new() {
62 initial_upload_directory = Some(dirs.home_dir().to_path_buf());
63 }
64 let buffer_update_view = Arc::new(eframe::epaint::mutex::Mutex::new(view));
65
66 let buffer_update_thread = Arc::new(Mutex::new(TerminalThread {
67 buffer_view: buffer_update_view.clone(),
68 capture_dialog: dialogs::capture_dialog::DialogState::default(),
69 auto_file_transfer: AutoFileTransfer::default(),
70 auto_transfer: None,
71 auto_login: None,
72 sound_thread: Arc::new(eframe::epaint::mutex::Mutex::new(SoundThread::new())),
73 terminal_type: None,
74 mouse_field: Vec::new(),
75 cache_directory: PathBuf::new(),
76 is_connected: false,
77 connection_time: Instant::now(),
78 current_transfer: TransferState::new(String::new()),
79 }));
80
81 let data = OpenConnectionData {
82 address: "".to_string(),
83 user_name: "".to_string(),
84 password: "".to_string(),
85 connection_type: ConnectionType::Telnet,
86 timeout: Duration::from_secs(1000),
87 use_ansi_music: icy_engine::ansi::MusicOption::Off,
88 baud_emulation: icy_engine::ansi::BaudEmulation::Off,
89 proxy_command: None,
90 term_caps: TermCaps {
91 window_size: (0, 0),
92 terminal: TerminalEmulation::Ascii,
93 },
94 modem: None,
95 };
96
97 let (update_thread_handle, tx, rx) = crate::ui::terminal_thread::start_update_thread(&cc.egui_ctx, data, buffer_update_thread.clone());
98 let mut parser = icy_engine::ansi::Parser::default();
99 parser.bs_is_ctrl_char = true;
100 let buffer_parser = Box::new(parser);
101 let mut view = MainWindow {
102 buffer_view: buffer_update_view.clone(),
103 //address_list: HoverList::new(),
104 state: MainWindowState { options, ..Default::default() },
105 initial_upload_directory,
106 screen_mode: ScreenMode::default(),
107 #[cfg(target_arch = "wasm32")]
108 poll_thread,
109 is_fullscreen_mode,
110 export_dialog: dialogs::export_dialog::DialogState::default(),
111 upload_dialog: dialogs::upload_dialog::DialogState::default(),
112 dialing_directory_dialog: dialogs::dialing_directory_dialog::DialogState::new(addresses),
113 drag_start: None,
114 last_pos: Position::default(),
115 terminal_thread: buffer_update_thread,
116 terminal_thread_handle: Some(update_thread_handle),
117 tx,
118 rx,
119 show_find_dialog: false,
120 find_dialog: dialogs::find_dialog::DialogState::default(),
121 shift_pressed_during_selection: false,
122 use_rip: false,
123 buffer_parser,
124 title: String::new(),
125 show_disconnect: false,
126 };
127
128 #[cfg(not(target_arch = "wasm32"))]
129 parse_command_line(&ctx, &mut view);
130
131 let mut style: egui::Style = (*ctx.style()).clone();
132 style.spacing.window_margin = egui::Margin::same(8.0);
133
134 // style.spacing.button_padding = Vec2::new(4., 2.);
135 style.text_styles = [
136 (Heading, FontId::new(24.0, Proportional)),
137 (Body, FontId::new(18.0, Proportional)),
138 (Monospace, FontId::new(18.0, egui::FontFamily::Monospace)),
139 (Button, FontId::new(18.0, Proportional)),
140 (Small, FontId::new(14.0, Proportional)),
141 ]
142 .into();
143 ctx.set_style(style);
144 view
145 }
146 }
147
148 #[cfg(not(target_arch = "wasm32"))]
149 fn parse_command_line(ctx: &egui::Context, view: &mut MainWindow) {
150 let args: Vec<String> = std::env::args().collect();
151 if let Some(arg) = args.get(1) {
152 view.dialing_directory_dialog.addresses.addresses[0].address = arg.clone();
153 view.call_bbs(ctx, 0);
154 }
155 }
156
157 impl eframe::App for MainWindow {
158 fn update(&mut self, ctx: &egui::Context, frame: &mut eframe::Frame) {
159 #[cfg(not(target_arch = "wasm32"))]
160 self.update_title(ctx);
161
162 ctx.input(|i| {
163 if let Some(or) = i.viewport().outer_rect {
164 if let Some(ir) = i.viewport().inner_rect {
165 let rect = Rect {
166 min: or.min,
167 max: (ir.max - ir.min).to_pos2(),
168 };
169 if self.state.options.window_rect != Some(rect) {
170 self.state.options.window_rect = Some(rect);
171 self.state.store_options();
172 }
173 }
174 }
175 });
176
177 match self.get_mode() {
178 MainWindowMode::ShowTerminal => {
179 self.handle_terminal_key_binds(ctx);
180 self.update_terminal_window(ctx, frame, false);
181 }
182 MainWindowMode::ShowDialingDirectory => {
183 self.update_terminal_window(ctx, frame, true);
184 }
185 MainWindowMode::ShowSettings => {
186 self.update_terminal_window(ctx, frame, false);
187 self.state.show_settings(ctx, frame);
188 }
189 MainWindowMode::DeleteSelectedAddress(uuid) => {
190 self.update_terminal_window(ctx, frame, true);
191 super::dialogs::show_delete_address_confirmation::show_dialog(self, ctx, uuid);
192 }
193
194 MainWindowMode::SelectProtocol(download) => {
195 self.update_terminal_window(ctx, frame, false);
196 dialogs::protocol_selector::view_selector(self, ctx, frame, download);
197 }
198
199 MainWindowMode::FileTransfer(download) => {
200 self.update_terminal_window(ctx, frame, false);
201 let state = self.terminal_thread.lock().current_transfer.clone();
202 // auto close uploads.
203 if !download && state.is_finished {
204 self.set_mode(ctx, MainWindowMode::ShowTerminal);
205 }
206 match dialogs::up_download_dialog::FileTransferDialog::new().show_dialog(ctx, frame, &state, download) {
207 dialogs::up_download_dialog::FileTransferDialogAction::Run => {}
208 dialogs::up_download_dialog::FileTransferDialogAction::Close | dialogs::up_download_dialog::FileTransferDialogAction::CancelTransfer => {
209 if state.is_finished {
210 self.set_mode(ctx, MainWindowMode::ShowTerminal);
211 } else {
212 self.send_data(super::connect::SendData::CancelTransfer);
213 self.set_mode(ctx, MainWindowMode::ShowTerminal);
214 }
215 }
216 }
217 }
218 MainWindowMode::ShowCaptureDialog => {
219 self.update_terminal_window(ctx, frame, false);
220 if !self.terminal_thread.lock().capture_dialog.show_caputure_dialog(ctx) {
221 self.set_mode(ctx, MainWindowMode::ShowTerminal);
222 }
223 }
224 MainWindowMode::ShowExportDialog => {
225 self.update_terminal_window(ctx, frame, false);
226 self.show_export_dialog(ctx);
227 }
228 MainWindowMode::ShowUploadDialog => {
229 self.update_terminal_window(ctx, frame, false);
230 self.show_upload_dialog(ctx);
231 }
232 MainWindowMode::ShowIEMSI => {
233 self.update_terminal_window(ctx, frame, false);
234 dialogs::show_iemsi::show_iemsi(self, ctx);
235 } // MainWindowMode::AskDeleteEntry => todo!(),
236
237 MainWindowMode::ShowDisconnectedMessage(time, system) => {
238 self.update_terminal_window(ctx, frame, false);
239 dialogs::show_disconnected_message::show_disconnected(self, ctx, time, system);
240 }
241 }
242 }
243
244 /* fn on_exit(&mut self, gl: Option<&glow::Context>) {
245 if let Some(gl) = gl {
246 self.buffer_view.lock().destroy(gl);
247 }
248 }*/
249 }