feat: option to enable global hotkey (#2665)

This commit is contained in:
Tunglies 2025-02-09 07:45:22 +08:00 committed by GitHub
parent 37e5c22a5a
commit 215dcee3f1
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
7 changed files with 69 additions and 7 deletions

View File

@ -99,6 +99,9 @@ pub struct IVerge {
/// format: {func},{key}
pub hotkeys: Option<Vec<String>>,
/// enable global hotkey
pub enable_global_hotkey: Option<bool>,
/// 切换代理时自动关闭连接
pub auto_close_connection: Option<bool>,
@ -279,6 +282,7 @@ impl IVerge {
webdav_username: None,
webdav_password: None,
enable_tray_speed: Some(true),
enable_global_hotkey: Some(true),
..Self::default()
}
}
@ -345,6 +349,7 @@ impl IVerge {
patch!(web_ui_list);
patch!(clash_core);
patch!(hotkeys);
patch!(enable_global_hotkey);
patch!(auto_close_connection);
patch!(auto_check_update);
@ -411,6 +416,7 @@ pub struct IVergeResponse {
pub enable_silent_start: Option<bool>,
pub enable_system_proxy: Option<bool>,
pub enable_proxy_guard: Option<bool>,
pub enable_global_hotkey: Option<bool>,
pub use_default_bypass: Option<bool>,
pub system_proxy_bypass: Option<String>,
pub proxy_guard_duration: Option<u64>,
@ -472,6 +478,7 @@ impl From<IVerge> for IVergeResponse {
enable_silent_start: verge.enable_silent_start,
enable_system_proxy: verge.enable_system_proxy,
enable_proxy_guard: verge.enable_proxy_guard,
enable_global_hotkey: verge.enable_global_hotkey,
use_default_bypass: verge.use_default_bypass,
system_proxy_bypass: verge.system_proxy_bypass,
proxy_guard_duration: verge.proxy_guard_duration,

View File

@ -45,6 +45,13 @@ impl Hotkey {
Ok(())
}
pub fn reset(&self) -> Result<()> {
let app_handle = handle::Handle::global().app_handle().unwrap();
let manager = app_handle.global_shortcut();
manager.unregister_all()?;
Ok(())
}
pub fn register(&self, hotkey: &str, func: &str) -> Result<()> {
let app_handle = handle::Handle::global().app_handle().unwrap();
let manager = app_handle.global_shortcut();
@ -76,7 +83,17 @@ impl Hotkey {
}
}
} else {
f();
if let Some(window) = app_handle.get_webview_window("main") {
let is_enable_global_hotkey = { Config::verge().latest().enable_global_hotkey} .unwrap();
let is_visible = window.is_visible().unwrap_or(false);
let is_focused = window.is_focused().unwrap_or(false);
if is_enable_global_hotkey {
f();
} else if is_focused && is_visible {
f();
}
}
}
}
});
@ -89,7 +106,6 @@ impl Hotkey {
let app_handle = handle::Handle::global().app_handle().unwrap();
let manager = app_handle.global_shortcut();
manager.unregister(hotkey)?;
log::debug!(target: "app", "unregister hotkey {hotkey}");
Ok(())
}

View File

@ -209,10 +209,12 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
let http_enabled = patch.verge_http_enabled;
let http_port = patch.verge_port;
let enable_tray_speed = patch.enable_tray_speed;
let enable_global_hotkey = patch.enable_global_hotkey;
let res: std::result::Result<(), anyhow::Error> = {
let mut should_restart_core = false;
let mut should_update_clash_config = false;
let mut should_update_verge_config = false;
let mut should_update_launch = false;
let mut should_update_sysproxy = false;
let mut should_update_systray_icon = false;
@ -226,12 +228,13 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
should_update_systray_tooltip = true;
should_update_systray_icon = true;
}
if enable_global_hotkey.is_some() {
should_update_verge_config = true;
}
#[cfg(not(target_os = "windows"))]
if redir_enabled.is_some() || redir_port.is_some() {
should_restart_core = true;
}
#[cfg(target_os = "linux")]
if tproxy_enabled.is_some() || tproxy_port.is_some() {
should_restart_core = true;
@ -286,6 +289,10 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
CoreManager::global().update_config().await?;
handle::Handle::refresh_clash();
}
if should_update_verge_config {
Config::verge().draft().enable_global_hotkey = enable_global_hotkey;
handle::Handle::refresh_verge();
}
if should_update_launch {
sysopt::Sysopt::global().update_launch()?;
}

View File

@ -6,6 +6,7 @@ mod feat;
mod utils;
use crate::core::hotkey;
use crate::utils::{resolve, resolve::resolve_scheme, server};
use config::Config;
use tauri_plugin_autostart::MacosLauncher;
use tauri_plugin_deep_link::DeepLinkExt;
@ -159,6 +160,12 @@ pub fn run() {
{
log_err!(hotkey::Hotkey::global().register("Control+Q", "quit"));
};
{
let is_enable_global_hotkey = { Config::verge().draft().enable_global_hotkey }.unwrap();
if !is_enable_global_hotkey {
log_err!(hotkey::Hotkey::global().init())
}
}
}
tauri::WindowEvent::Focused(false) => {
#[cfg(target_os = "macos")]
@ -169,6 +176,14 @@ pub fn run() {
{
log_err!(hotkey::Hotkey::global().unregister("Control+Q"));
};
{
let is_enable_global_hotkey = { Config::verge().draft().enable_global_hotkey }.unwrap();
if !is_enable_global_hotkey {
log_err!(hotkey::Hotkey::global().reset())
} else {
log_err!(hotkey::Hotkey::global().init())
}
}
}
tauri::WindowEvent::Destroyed => {
#[cfg(target_os = "macos")]

View File

@ -1,7 +1,7 @@
import { forwardRef, useImperativeHandle, useState } from "react";
import { useTranslation } from "react-i18next";
import { useLockFn } from "ahooks";
import { styled, Typography } from "@mui/material";
import { styled, Typography, Switch } from "@mui/material";
import { useVerge } from "@/hooks/use-verge";
import { BaseDialog, DialogRef, Notice } from "@/components/base";
import { HotkeyInput } from "./hotkey-input";
@ -29,6 +29,9 @@ export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
const { verge, patchVerge } = useVerge();
const [hotkeyMap, setHotkeyMap] = useState<Record<string, string[]>>({});
const [enableGlobalHotkey, setEnableHotkey] = useState(
verge?.enable_global_hotkey ?? true,
);
useImperativeHandle(ref, () => ({
open: () => {
@ -69,7 +72,10 @@ export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
.filter(Boolean);
try {
await patchVerge({ hotkeys });
await patchVerge({
hotkeys,
enable_global_hotkey: enableGlobalHotkey,
});
setOpen(false);
} catch (err: any) {
Notice.error(err.message || err.toString());
@ -80,13 +86,22 @@ export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
<BaseDialog
open={open}
title={t("Hotkey Setting")}
contentSx={{ width: 450, maxHeight: 330 }}
contentSx={{ width: 450, maxHeight: 380 }}
okBtn={t("Save")}
cancelBtn={t("Cancel")}
onClose={() => setOpen(false)}
onCancel={() => setOpen(false)}
onOk={onSave}
>
<ItemWrapper style={{ marginBottom: 16 }}>
<Typography>{t("Enable Global Hotkey")}</Typography>
<Switch
edge="end"
checked={enableGlobalHotkey}
onChange={(e) => setEnableHotkey(e.target.checked)}
/>
</ItemWrapper>
{HOTKEY_FUNC.map((func) => (
<ItemWrapper key={func}>
<Typography>{t(func)}</Typography>

View File

@ -328,6 +328,7 @@
"Default Latency Test Info": "仅用于 HTTP 客户端请求测试,不会对配置文件产生影响",
"Default Latency Timeout": "测试超时时间",
"Hotkey Setting": "热键设置",
"Enable Global Hotkey": "启用全局热键",
"open_or_close_dashboard": "打开/关闭面板",
"clash_mode_rule": "规则模式",
"clash_mode_global": "全局模式",

View File

@ -711,6 +711,7 @@ interface IVergeConfig {
enable_auto_launch?: boolean;
enable_silent_start?: boolean;
enable_system_proxy?: boolean;
enable_global_hotkey?: boolean;
proxy_auto_config?: boolean;
pac_file_content?: string;
enable_random_port?: boolean;