diff --git a/src/module/screen_shotter.rs b/src/module/screen_shotter.rs index a221103..ee547cf 100644 --- a/src/module/screen_shotter.rs +++ b/src/module/screen_shotter.rs @@ -4,7 +4,7 @@ mod toolbar; use arboard::Clipboard; use image::{self, GenericImageView, Rgba}; use std::{sync::{Arc, Mutex, mpsc, mpsc::Sender}, collections::HashMap}; -use slint::{SharedPixelBuffer, Rgba8Pixel}; +use slint::{Rgba8Pixel, SharedPixelBuffer, Weak}; use i_slint_backend_winit::{winit::platform::windows::WindowExtWindows, WinitWindowAccessor}; use global_hotkey::hotkey::{HotKey, Modifiers, Code}; use xcap::Monitor; @@ -26,9 +26,8 @@ pub enum PinOperation { pub enum ShotterMessage { Move(u32), Close(u32), - ShowToolbar(i32, i32), - MoveToolbar(f32, f32), - FinishToolbar(u32), + ShowToolbar(i32, i32, u32, Weak), + HideToolbar(bool), OperatePin(u32, PinOperation), } @@ -189,7 +188,8 @@ impl ScreenShotter{ let pin_win = PinWin::new( bac_buffer_rc.clone(), rect, mask_win.get_offset_x(), mask_win.get_offset_y(), - *max_pin_win_id, message_sender_clone); + *max_pin_win_id, message_sender_clone + ); let pin_window_clone = pin_win.pin_window.as_weak(); @@ -214,54 +214,59 @@ impl ScreenShotter{ // event listen let pin_windows_clone = pin_windows.clone(); // let pin_wins_clone = pin_wins.clone(); - let toolbar_window_clone = toolbar.get_window(); + let toolbar_window_clone: slint::Weak = toolbar.get_window(); std::thread::spawn(move || { loop { if let Ok(message) = message_reciever.recv() { match message { ShotterMessage::Move(id) => { - ScreenShotter::pin_win_move_hander(pin_windows.clone(), id); + ScreenShotter::pin_win_move_hander(pin_windows.clone(), id, toolbar_window_clone.clone()); }, ShotterMessage::Close(id) => { pin_windows_clone.lock().unwrap().remove(&id); // pin_wins_clone.lock().unwrap().remove(&id); // TODO: clear pin_wins }, - ShotterMessage::ShowToolbar(x, y) => { + ShotterMessage::ShowToolbar(x, y, id, pin_window) => { toolbar_window_clone.upgrade_in_event_loop(move |win| { - win.invoke_show_pos(x, y); + win.invoke_show_pos(x, y, id as i32); }).unwrap(); - }, - ShotterMessage::MoveToolbar(x, y) => { - toolbar_window_clone.upgrade_in_event_loop(move |win| { - win.invoke_move(x, y); + // focus the pin window + pin_window.upgrade_in_event_loop(move |win| { + win.window().with_winit_window(|winit_win: &i_slint_backend_winit::winit::window::Window| { + winit_win.focus_window(); + winit_win.request_redraw(); // TODO to fix the error win size + }); }).unwrap(); }, - ShotterMessage::FinishToolbar(id) => { + ShotterMessage::HideToolbar(if_force) => { toolbar_window_clone.upgrade_in_event_loop(move |win| { - win.invoke_finish(id as i32); + win.invoke_try_hide(if_force); }).unwrap(); }, ShotterMessage::OperatePin(id, operation) => { + // toolbar_window_clone.upgrade_in_event_loop(move |win| { + // win.invoke_try_hide(true); + // }).unwrap(); // TODO del? let pin_windows = pin_windows_clone.lock().unwrap(); - if let Some(pin_win) = pin_windows.get(&id) { + if let Some(pin_window) = pin_windows.get(&id) { match operation { PinOperation::Close() => { - pin_win.upgrade_in_event_loop(move |win| { + pin_window.upgrade_in_event_loop(move |win| { win.invoke_close(); }).unwrap(); }, PinOperation::Hide() => { - pin_win.upgrade_in_event_loop(move |win| { + pin_window.upgrade_in_event_loop(move |win| { win.invoke_hide(); }).unwrap(); }, PinOperation::Save() => { - pin_win.upgrade_in_event_loop(move |win| { + pin_window.upgrade_in_event_loop(move |win| { win.invoke_save(); }).unwrap(); }, PinOperation::Copy() => { - pin_win.upgrade_in_event_loop(move |win| { + pin_window.upgrade_in_event_loop(move |win| { win.invoke_copy(); }).unwrap(); }, @@ -282,22 +287,24 @@ impl ScreenShotter{ } } - fn pin_win_move_hander(pin_windows: Arc>>>, move_win_id: u32) { + fn pin_win_move_hander(pin_windows: Arc>>>, move_win_id: u32, toolbar_window: slint::Weak) { slint::invoke_from_event_loop(move || { let padding = 10; let pin_windows = pin_windows.lock().unwrap(); let move_win = &pin_windows[&move_win_id].unwrap(); + + let move_pos = move_win.window().position(); + let move_size = move_win.window().size(); + let move_bottom = move_pos.y + move_size.height as i32; + let move_right = move_pos.x + move_size.width as i32; + + toolbar_window.unwrap().invoke_win_move(move_right, move_bottom); + for pin_win_id in pin_windows.keys(){ if move_win_id != *pin_win_id { let other_win = &pin_windows[pin_win_id].unwrap(); - - let move_pos = move_win.window().position(); - let move_size = move_win.window().size(); let other_pos = other_win.window().position(); let other_size = other_win.window().size(); - - let move_bottom = move_pos.y + move_size.height as i32; - let move_right = move_pos.x + move_size.width as i32; let other_bottom = other_pos.y + other_size.height as i32; let other_right = other_pos.x + other_size.width as i32; @@ -334,7 +341,9 @@ impl ScreenShotter{ } } - move_win.window().set_position(slint::PhysicalPosition::new(move_pos.x - delta_x, move_pos.y - delta_y)); + if delta_x != 0 || delta_y != 0 { + move_win.window().set_position(slint::PhysicalPosition::new(move_pos.x - delta_x, move_pos.y - delta_y)); + } } } }).unwrap(); diff --git a/src/module/screen_shotter/pin_win.rs b/src/module/screen_shotter/pin_win.rs index 3aa81d9..9d89791 100644 --- a/src/module/screen_shotter/pin_win.rs +++ b/src/module/screen_shotter/pin_win.rs @@ -37,7 +37,6 @@ impl PinWin { let pin_window_clone = pin_window.as_weak(); let message_sender_clone = message_sender.clone(); pin_window.on_win_move(move |mut delta_x, mut delta_y| { - let pin_window_clone = pin_window_clone.unwrap(); let now_pos = pin_window_clone.window().position().to_logical(pin_window_clone.window().scale_factor()); let is_stick_x = pin_window_clone.get_is_stick_x(); @@ -67,17 +66,46 @@ impl PinWin { }); } + { // code for focuse change + let pin_window_clone = pin_window.as_weak(); + let message_sender_clone = message_sender.clone(); + pin_window.on_focus_trick( + move |has_focus| { + let pin_window = pin_window_clone.unwrap(); + if has_focus { + let position = pin_window.window().position(); + let scale_factor = pin_window.get_scale_factor(); + let width = pin_window.get_win_width(); + let height = pin_window.get_win_height(); + let left_bottom_x = position.x + (width * scale_factor) as i32; + let left_bottom_y = position.y + (height * scale_factor) as i32; + message_sender_clone.send(ShotterMessage::ShowToolbar(left_bottom_x, left_bottom_y, id, pin_window.as_weak())).unwrap(); + } else { + if pin_window.window().is_visible() == false || pin_window.window().is_minimized() { + message_sender_clone.send(ShotterMessage::HideToolbar(true)).unwrap(); + } else { + message_sender_clone.send(ShotterMessage::HideToolbar(false)).unwrap(); + } + } + true + } + ); + } + { // code for function - { // for close + { // for close and hide let pin_window_clone = pin_window.as_weak(); let message_sender_clone = message_sender.clone(); pin_window.on_close(move || { + message_sender_clone.send(ShotterMessage::HideToolbar(true)).unwrap(); pin_window_clone.unwrap().hide().unwrap(); message_sender_clone.send(ShotterMessage::Close(id)).unwrap(); }); let pin_window_clone = pin_window.as_weak(); + let message_sender_clone = message_sender.clone(); pin_window.on_hide(move || { + message_sender_clone.send(ShotterMessage::HideToolbar(true)).unwrap(); pin_window_clone.unwrap().window().with_winit_window(|winit_win: &i_slint_backend_winit::winit::window::Window| { winit_win.set_minimized(true); }); @@ -102,26 +130,25 @@ impl PinWin { ); img = img.crop(img_x as u32, img_y as u32, img_width as u32, img_height as u32); - let app_config = AppConfig::global().lock().unwrap(); - let save_path = app_config.get_save_path(); - - let file_name = chrono::Local::now().format("Rotor_%Y-%m-%d-%H-%M-%S.png").to_string(); - let params = DialogParams { - title: "Select an image to save", - file_types: vec![("PNG Files", "*.png")], - default_extension: "png", - file_name: &file_name, - default_folder: &save_path, - ..Default::default() - }; - pin_window_clone.unwrap().hide().unwrap(); - - let dialog_result = wfd::save_dialog(params); - if let Ok(file_path_result) = dialog_result { - img.save(file_path_result.selected_file_path).unwrap(); - } - pin_window_clone.unwrap().invoke_close(); + + std::thread::spawn(move || { + let app_config = AppConfig::global().lock().unwrap(); + let save_path = app_config.get_save_path(); + let file_name = chrono::Local::now().format("Rotor_%Y-%m-%d-%H-%M-%S.png").to_string(); + let params = DialogParams { + title: "Select an image to save", + file_types: vec![("PNG Files", "*.png")], + default_extension: "png", + file_name: &file_name, + default_folder: &save_path, + ..Default::default() + }; + let dialog_result = wfd::save_dialog(params); + if let Ok(file_path_result) = dialog_result { + img.save(file_path_result.selected_file_path).unwrap(); + } + }); }); //copy @@ -135,43 +162,18 @@ impl PinWin { ).unwrap() ); img = img.crop(img_x as u32, img_y as u32, img_width as u32, img_height as u32); - - let mut clipboard = Clipboard::new().unwrap(); - let img_data = ImageData { - width: img.width() as usize, - height: img.height() as usize, - bytes: Cow::from(img.to_rgba8().to_vec()) - }; - clipboard.set_image(img_data).unwrap(); pin_window_clone.unwrap().invoke_close(); - }); - } - { // for show toolbar - let pin_window_clone = pin_window.as_weak(); - let message_sender_clone = message_sender.clone(); - pin_window.on_show_toolbar(move |x, y| { - let pin_window = pin_window_clone.unwrap(); - let position = pin_window.window().position(); - let scale = pin_window.window().scale_factor(); - let center_x = position.x + (x * scale) as i32; - let center_y = position.y + (y * scale) as i32; - message_sender_clone.send(ShotterMessage::ShowToolbar(center_x, center_y)).unwrap(); - }); - } - - { // for move toolbar - let message_sender_clone = message_sender.clone(); - pin_window.on_move_toolbar(move |x, y| { - message_sender_clone.send(ShotterMessage::MoveToolbar(x, y)).unwrap(); - }); - } - - { // for finish toolbar - let message_sender_clone = message_sender.clone(); - pin_window.on_finish_toolbar(move || { - message_sender_clone.send(ShotterMessage::FinishToolbar(id)).unwrap(); + std::thread::spawn(move || { + let mut clipboard = Clipboard::new().unwrap(); + let img_data = ImageData { + width: img.width() as usize, + height: img.height() as usize, + bytes: Cow::from(img.to_rgba8().to_vec()) + }; + clipboard.set_image(img_data).unwrap(); + }); }); } } @@ -206,11 +208,13 @@ slint::slint! { export component PinWindow inherits Window { no-frame: true; - always-on-top: true; title: "小云视窗"; - forward-focus: key_focus; + forward-focus: key-focus; icon: @image-url("assets/logo.png"); + pure callback focus_trick(bool) -> bool; + always-on-top: focus_trick(key-focus.has-focus); + in property bac_image; in property win_border_width: 1px; in property scale_factor; diff --git a/src/module/screen_shotter/toolbar.rs b/src/module/screen_shotter/toolbar.rs index 14dc56c..121fd9d 100644 --- a/src/module/screen_shotter/toolbar.rs +++ b/src/module/screen_shotter/toolbar.rs @@ -16,55 +16,80 @@ impl Toolbar { winit_win.set_skip_taskbar(true); }); - let toolbar_window_clone = toolbar_window.as_weak(); - toolbar_window.on_show_pos(move |x, y| { - let toolbar_window = toolbar_window_clone.unwrap(); - - let height = toolbar_window.get_win_height() as f32; - let width = toolbar_window.get_win_width() as f32; - let scale = toolbar_window.window().scale_factor(); - let x_pos = x - (width * scale / 2.0) as i32; - let y_pos = y - (height * scale / 2.0) as i32; - toolbar_window.window().set_position(slint::WindowPosition::Physical(slint::PhysicalPosition::new(x_pos, y_pos))); - toolbar_window.show().unwrap(); - - toolbar_window.window().with_winit_window(|winit_win: &i_slint_backend_winit::winit::window::Window| { - winit_win.focus_window(); + { // code for show + let toolbar_window_clone = toolbar_window.as_weak(); + toolbar_window.on_show_pos(move |x, y, id| { + let toolbar_window = toolbar_window_clone.unwrap(); + let win_size = toolbar_window.window().size(); + let x_pos = x - win_size.width as i32; + let y_pos = y; + toolbar_window.window().set_position(slint::WindowPosition::Physical(slint::PhysicalPosition::new(x_pos, y_pos + 2))); + toolbar_window.set_pin_focused(true); + toolbar_window.set_id(id); + + println!("{}", toolbar_window.window().is_visible()); + if toolbar_window.window().is_visible() == false { + toolbar_window.show().unwrap(); + } }); - }); + } - let toolbar_window_clone = toolbar_window.as_weak(); - toolbar_window.on_move(move |x, y| { - let toolbar_window = toolbar_window_clone.unwrap(); - let r = x * x + y * y; - let mut angle = y.atan2(x) * 180.0 / 3.14 + 135.0; - if angle < 0.0 { angle += 360.0; } - if r < (50 * 50) as f32 { - toolbar_window.set_active_num(-1); - } else { - let active_num = (angle / 90.0) as i32; - toolbar_window.set_active_num(active_num); - } - }); + { // code for click + let message_sender_clone = message_sender.clone(); + toolbar_window.on_click(move |id, active_num| { + if active_num == 0 { + message_sender_clone.send(ShotterMessage::OperatePin(id as u32, PinOperation::Hide())).unwrap(); + } else if active_num == 1 { + message_sender_clone.send(ShotterMessage::OperatePin(id as u32, PinOperation::Copy())).unwrap(); + } else if active_num == 2 { + message_sender_clone.send(ShotterMessage::OperatePin(id as u32, PinOperation::Close())).unwrap(); + } else if active_num == 3 { + message_sender_clone.send(ShotterMessage::OperatePin(id as u32, PinOperation::Save())).unwrap(); + } + }); + } - let toolbar_window_clone = toolbar_window.as_weak(); - let message_sender_clone = message_sender.clone(); - toolbar_window.on_finish(move |id| { - let toolbar_window = toolbar_window_clone.unwrap(); - let active_num = toolbar_window.get_active_num(); - - if active_num == 0 { - message_sender_clone.send(ShotterMessage::OperatePin(id as u32, PinOperation::Hide())).unwrap(); - } else if active_num == 1 { - message_sender_clone.send(ShotterMessage::OperatePin(id as u32, PinOperation::Copy())).unwrap(); - } else if active_num == 2 { - message_sender_clone.send(ShotterMessage::OperatePin(id as u32, PinOperation::Close())).unwrap(); - } else if active_num == 3 { - message_sender_clone.send(ShotterMessage::OperatePin(id as u32, PinOperation::Save())).unwrap(); - } + { // code for hide + let toolbar_window_clone = toolbar_window.as_weak(); + toolbar_window.on_try_hide(move |if_force| { + let toolbar_window = toolbar_window_clone.unwrap(); + if if_force == false { + let toolbar_focused = toolbar_window.get_toolbar_focused(); + let pin_focused = toolbar_window.get_pin_focused(); + if toolbar_focused || pin_focused { + toolbar_window.set_pin_focused(false); + return; + } + } + toolbar_window.hide().unwrap(); + }); + } - toolbar_window_clone.unwrap().hide().unwrap(); - }); + { // code for focuse change + let toolbar_window_clone = toolbar_window.as_weak(); + toolbar_window.on_focus_trick( + move |pin_focused, toolbar_focused| { + if pin_focused || toolbar_focused { return true; } + let toolbar_window = toolbar_window_clone.unwrap(); + toolbar_window.set_id(-1); + toolbar_window.hide().unwrap(); + true + } + ); + } + + { // code for win move + let toolbar_window_clone = toolbar_window.as_weak(); + toolbar_window.on_win_move(move |x, y| { + let toolbar_window = toolbar_window_clone.unwrap(); + let scale_factor = toolbar_window.window().scale_factor(); + let win_width = toolbar_window.get_win_width() as f32 * scale_factor; + let x_pos = x - win_width as i32; + let y_pos = y; + toolbar_window.window().set_position(slint::WindowPosition::Physical(slint::PhysicalPosition::new(x_pos, y_pos + 2))); + toolbar_window.show().unwrap(); + }); + } Toolbar { toolbar_window, @@ -85,112 +110,72 @@ slint::slint! { name: string, } - component Arc inherits Path { - in-out property active: false; - in-out property out_radius: 100; - in-out property inner_radius: 50; - in-out property out_start_x; - in-out property out_start_y; - in-out property out_end_x; - in-out property out_end_y; - in-out property inner_start_x; - in-out property inner_start_y; - in-out property inner_end_x; - in-out property inner_end_y; - - width: 100%; - height: 100%; - viewbox-width: self.width / 1px; - viewbox-height: self.height / 1px; - fill: active ? rgb(0, 175, 255).with-alpha(0.5) : transparent; - stroke: rgb(0, 175, 255); - stroke-width: 2px; - - commands: @tr("M {} {} L {} {} A {} {} 0 0 0 {} {} L {} {} A {} {} 0 0 0 {} {}", - inner_start_x, inner_start_y, - out_start_x, out_start_y, - out_radius, out_radius, out_end_x, out_end_y, - inner_end_x, inner_end_y, - inner_radius, inner_radius, inner_start_x, inner_start_y - ); - } + component ToolBtn inherits Rectangle { // TODO merge with TitleBtn + in property icon <=> image.source; + in property hover_color: Palette.accent-background; + callback clicked <=> touch.clicked; - component ArcBtn inherits Rectangle{ - in-out property icon; - in-out property out_radius: 80; - in-out property inner_radius: 50; - in-out property begin_angle; - in-out property end_angle; - in-out property active: false; - - Arc { - active: active; - out_radius: out_radius; - inner_radius: inner_radius; - out_start_x: (out_radius) + sin(begin_angle * 1deg) * out_radius; - out_start_y: (out_radius) - cos(begin_angle * 1deg) * out_radius; - out_end_x: (out_radius) + sin(end_angle * 1deg) * out_radius; - out_end_y: (out_radius) - cos(end_angle * 1deg) * out_radius; - inner_start_x: (out_radius) + sin(begin_angle * 1deg) * inner_radius; - inner_start_y: (out_radius) - cos(begin_angle * 1deg) * inner_radius; - inner_end_x: (out_radius) + sin(end_angle * 1deg) * inner_radius; - inner_end_y: (out_radius) - cos(end_angle * 1deg) * inner_radius; - } + width: 40px; + background: transparent; + animate background { duration: 150ms; } - Image { - colorize: active ? rgb(0, 175, 255) : Palette.foreground; - source: icon; - height: root.height / 8; - width: root.width / 8; - x: ((out_radius) + sin((begin_angle + end_angle) / 2 * 1deg) * (out_radius + inner_radius) / 2) * 1px - self.width / 2; - y: ((out_radius) - cos((begin_angle + end_angle) / 2 * 1deg) * (out_radius + inner_radius) / 2) * 1px - self.height / 2; + touch := TouchArea { + image := Image { + height: 16px; + width: 16px; + colorize: Palette.foreground; + } } + + states [ + pressed when touch.pressed : { + root.background: hover_color.darker(0.5); + } + hover when touch.has-hover : { + root.background: hover_color; + } + ] } export component ToolbarWindow inherits Window { - background: transparent; no-frame: true; - title: "菜单栏"; - always-on-top: true; + height: 40px; + forward-focus: focus_scope; + + in-out property win_width: tools.length * 40; + width: win_width * 1px; - in-out property win_height: 160; - in-out property win_width: 160; - in-out property active_num: -1; + pure callback focus_trick(bool, bool) -> bool; + always-on-top: focus_trick(pin_focused, toolbar_focused); + + in-out property pin_focused: false; + in-out property toolbar_focused: focus_scope.has-focus; + in-out property id: -1; in-out property <[Tool_slint]> tools: [ { id: 0, icon: @image-url("./assets/icon/min.svg"), name: "最小化" }, - { id: 1, icon: @image-url("./assets/icon/right.svg"), name: "复制" }, - { id: 2, icon: @image-url("./assets/icon/close.svg"), name: "关闭" }, { id: 3, icon: @image-url("./assets/icon/save.svg"), name: "保存" }, + { id: 2, icon: @image-url("./assets/icon/close.svg"), name: "关闭" }, + { id: 1, icon: @image-url("./assets/icon/right.svg"), name: "复制" }, ]; - width: win_width * 1px; - height: win_height * 1px; - - callback show_pos(int, int); - callback move(float, float); - callback finish(int); - - Rectangle { - height: 100%; - width: 100%; - border-radius: self.height / 2; - background: Palette.background.with-alpha(0.5); - - for tool in root.tools: ArcBtn{ - icon: tool.icon; - begin_angle: 360/tools.length * (tool.id) - 360/tools.length/2; - end_angle: 360/tools.length * (tool.id) + 360/tools.length/2; - active: active_num == tool.id; - } - - tool-tips := Text { - font-size: 14px; - text: (active_num == -1) ? "" : tools[active_num].name; - height: root.height; - width: root.width; - vertical-alignment: center; - horizontal-alignment: center; + callback show_pos(int, int, int); + callback try_hide(bool); + callback win_move(int, int); + callback click(int, int); + callback key_released(KeyEvent); + + focus_scope := FocusScope { + HorizontalLayout { + spacing: 0; + padding: 0; + + for tool in root.tools: ToolBtn{ + width: 40px; + height: 40px; + icon: tool.icon; + clicked => { root.click(root.id, tool.id); } + } } } }