mirror of
https://github.com/clash-verge-rev/clash-verge-rev
synced 2025-05-05 16:03:44 +08:00
feat: option to enable global hotkey (#2665)
This commit is contained in:
parent
37e5c22a5a
commit
215dcee3f1
@ -99,6 +99,9 @@ pub struct IVerge {
|
|||||||
/// format: {func},{key}
|
/// format: {func},{key}
|
||||||
pub hotkeys: Option<Vec<String>>,
|
pub hotkeys: Option<Vec<String>>,
|
||||||
|
|
||||||
|
/// enable global hotkey
|
||||||
|
pub enable_global_hotkey: Option<bool>,
|
||||||
|
|
||||||
/// 切换代理时自动关闭连接
|
/// 切换代理时自动关闭连接
|
||||||
pub auto_close_connection: Option<bool>,
|
pub auto_close_connection: Option<bool>,
|
||||||
|
|
||||||
@ -279,6 +282,7 @@ impl IVerge {
|
|||||||
webdav_username: None,
|
webdav_username: None,
|
||||||
webdav_password: None,
|
webdav_password: None,
|
||||||
enable_tray_speed: Some(true),
|
enable_tray_speed: Some(true),
|
||||||
|
enable_global_hotkey: Some(true),
|
||||||
..Self::default()
|
..Self::default()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -345,6 +349,7 @@ impl IVerge {
|
|||||||
patch!(web_ui_list);
|
patch!(web_ui_list);
|
||||||
patch!(clash_core);
|
patch!(clash_core);
|
||||||
patch!(hotkeys);
|
patch!(hotkeys);
|
||||||
|
patch!(enable_global_hotkey);
|
||||||
|
|
||||||
patch!(auto_close_connection);
|
patch!(auto_close_connection);
|
||||||
patch!(auto_check_update);
|
patch!(auto_check_update);
|
||||||
@ -411,6 +416,7 @@ pub struct IVergeResponse {
|
|||||||
pub enable_silent_start: Option<bool>,
|
pub enable_silent_start: Option<bool>,
|
||||||
pub enable_system_proxy: Option<bool>,
|
pub enable_system_proxy: Option<bool>,
|
||||||
pub enable_proxy_guard: Option<bool>,
|
pub enable_proxy_guard: Option<bool>,
|
||||||
|
pub enable_global_hotkey: Option<bool>,
|
||||||
pub use_default_bypass: Option<bool>,
|
pub use_default_bypass: Option<bool>,
|
||||||
pub system_proxy_bypass: Option<String>,
|
pub system_proxy_bypass: Option<String>,
|
||||||
pub proxy_guard_duration: Option<u64>,
|
pub proxy_guard_duration: Option<u64>,
|
||||||
@ -472,6 +478,7 @@ impl From<IVerge> for IVergeResponse {
|
|||||||
enable_silent_start: verge.enable_silent_start,
|
enable_silent_start: verge.enable_silent_start,
|
||||||
enable_system_proxy: verge.enable_system_proxy,
|
enable_system_proxy: verge.enable_system_proxy,
|
||||||
enable_proxy_guard: verge.enable_proxy_guard,
|
enable_proxy_guard: verge.enable_proxy_guard,
|
||||||
|
enable_global_hotkey: verge.enable_global_hotkey,
|
||||||
use_default_bypass: verge.use_default_bypass,
|
use_default_bypass: verge.use_default_bypass,
|
||||||
system_proxy_bypass: verge.system_proxy_bypass,
|
system_proxy_bypass: verge.system_proxy_bypass,
|
||||||
proxy_guard_duration: verge.proxy_guard_duration,
|
proxy_guard_duration: verge.proxy_guard_duration,
|
||||||
|
@ -45,6 +45,13 @@ impl Hotkey {
|
|||||||
Ok(())
|
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<()> {
|
pub fn register(&self, hotkey: &str, func: &str) -> Result<()> {
|
||||||
let app_handle = handle::Handle::global().app_handle().unwrap();
|
let app_handle = handle::Handle::global().app_handle().unwrap();
|
||||||
let manager = app_handle.global_shortcut();
|
let manager = app_handle.global_shortcut();
|
||||||
@ -76,7 +83,17 @@ impl Hotkey {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} 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 app_handle = handle::Handle::global().app_handle().unwrap();
|
||||||
let manager = app_handle.global_shortcut();
|
let manager = app_handle.global_shortcut();
|
||||||
manager.unregister(hotkey)?;
|
manager.unregister(hotkey)?;
|
||||||
|
|
||||||
log::debug!(target: "app", "unregister hotkey {hotkey}");
|
log::debug!(target: "app", "unregister hotkey {hotkey}");
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -209,10 +209,12 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
|
|||||||
let http_enabled = patch.verge_http_enabled;
|
let http_enabled = patch.verge_http_enabled;
|
||||||
let http_port = patch.verge_port;
|
let http_port = patch.verge_port;
|
||||||
let enable_tray_speed = patch.enable_tray_speed;
|
let enable_tray_speed = patch.enable_tray_speed;
|
||||||
|
let enable_global_hotkey = patch.enable_global_hotkey;
|
||||||
|
|
||||||
let res: std::result::Result<(), anyhow::Error> = {
|
let res: std::result::Result<(), anyhow::Error> = {
|
||||||
let mut should_restart_core = false;
|
let mut should_restart_core = false;
|
||||||
let mut should_update_clash_config = 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_launch = false;
|
||||||
let mut should_update_sysproxy = false;
|
let mut should_update_sysproxy = false;
|
||||||
let mut should_update_systray_icon = 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_tooltip = true;
|
||||||
should_update_systray_icon = true;
|
should_update_systray_icon = true;
|
||||||
}
|
}
|
||||||
|
if enable_global_hotkey.is_some() {
|
||||||
|
should_update_verge_config = true;
|
||||||
|
}
|
||||||
#[cfg(not(target_os = "windows"))]
|
#[cfg(not(target_os = "windows"))]
|
||||||
if redir_enabled.is_some() || redir_port.is_some() {
|
if redir_enabled.is_some() || redir_port.is_some() {
|
||||||
should_restart_core = true;
|
should_restart_core = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(target_os = "linux")]
|
#[cfg(target_os = "linux")]
|
||||||
if tproxy_enabled.is_some() || tproxy_port.is_some() {
|
if tproxy_enabled.is_some() || tproxy_port.is_some() {
|
||||||
should_restart_core = true;
|
should_restart_core = true;
|
||||||
@ -286,6 +289,10 @@ pub async fn patch_verge(patch: IVerge) -> Result<()> {
|
|||||||
CoreManager::global().update_config().await?;
|
CoreManager::global().update_config().await?;
|
||||||
handle::Handle::refresh_clash();
|
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 {
|
if should_update_launch {
|
||||||
sysopt::Sysopt::global().update_launch()?;
|
sysopt::Sysopt::global().update_launch()?;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ mod feat;
|
|||||||
mod utils;
|
mod utils;
|
||||||
use crate::core::hotkey;
|
use crate::core::hotkey;
|
||||||
use crate::utils::{resolve, resolve::resolve_scheme, server};
|
use crate::utils::{resolve, resolve::resolve_scheme, server};
|
||||||
|
use config::Config;
|
||||||
use tauri_plugin_autostart::MacosLauncher;
|
use tauri_plugin_autostart::MacosLauncher;
|
||||||
use tauri_plugin_deep_link::DeepLinkExt;
|
use tauri_plugin_deep_link::DeepLinkExt;
|
||||||
|
|
||||||
@ -159,6 +160,12 @@ pub fn run() {
|
|||||||
{
|
{
|
||||||
log_err!(hotkey::Hotkey::global().register("Control+Q", "quit"));
|
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) => {
|
tauri::WindowEvent::Focused(false) => {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
@ -169,6 +176,14 @@ pub fn run() {
|
|||||||
{
|
{
|
||||||
log_err!(hotkey::Hotkey::global().unregister("Control+Q"));
|
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 => {
|
tauri::WindowEvent::Destroyed => {
|
||||||
#[cfg(target_os = "macos")]
|
#[cfg(target_os = "macos")]
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
import { forwardRef, useImperativeHandle, useState } from "react";
|
import { forwardRef, useImperativeHandle, useState } from "react";
|
||||||
import { useTranslation } from "react-i18next";
|
import { useTranslation } from "react-i18next";
|
||||||
import { useLockFn } from "ahooks";
|
import { useLockFn } from "ahooks";
|
||||||
import { styled, Typography } from "@mui/material";
|
import { styled, Typography, Switch } from "@mui/material";
|
||||||
import { useVerge } from "@/hooks/use-verge";
|
import { useVerge } from "@/hooks/use-verge";
|
||||||
import { BaseDialog, DialogRef, Notice } from "@/components/base";
|
import { BaseDialog, DialogRef, Notice } from "@/components/base";
|
||||||
import { HotkeyInput } from "./hotkey-input";
|
import { HotkeyInput } from "./hotkey-input";
|
||||||
@ -29,6 +29,9 @@ export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
const { verge, patchVerge } = useVerge();
|
const { verge, patchVerge } = useVerge();
|
||||||
|
|
||||||
const [hotkeyMap, setHotkeyMap] = useState<Record<string, string[]>>({});
|
const [hotkeyMap, setHotkeyMap] = useState<Record<string, string[]>>({});
|
||||||
|
const [enableGlobalHotkey, setEnableHotkey] = useState(
|
||||||
|
verge?.enable_global_hotkey ?? true,
|
||||||
|
);
|
||||||
|
|
||||||
useImperativeHandle(ref, () => ({
|
useImperativeHandle(ref, () => ({
|
||||||
open: () => {
|
open: () => {
|
||||||
@ -69,7 +72,10 @@ export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await patchVerge({ hotkeys });
|
await patchVerge({
|
||||||
|
hotkeys,
|
||||||
|
enable_global_hotkey: enableGlobalHotkey,
|
||||||
|
});
|
||||||
setOpen(false);
|
setOpen(false);
|
||||||
} catch (err: any) {
|
} catch (err: any) {
|
||||||
Notice.error(err.message || err.toString());
|
Notice.error(err.message || err.toString());
|
||||||
@ -80,13 +86,22 @@ export const HotkeyViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
<BaseDialog
|
<BaseDialog
|
||||||
open={open}
|
open={open}
|
||||||
title={t("Hotkey Setting")}
|
title={t("Hotkey Setting")}
|
||||||
contentSx={{ width: 450, maxHeight: 330 }}
|
contentSx={{ width: 450, maxHeight: 380 }}
|
||||||
okBtn={t("Save")}
|
okBtn={t("Save")}
|
||||||
cancelBtn={t("Cancel")}
|
cancelBtn={t("Cancel")}
|
||||||
onClose={() => setOpen(false)}
|
onClose={() => setOpen(false)}
|
||||||
onCancel={() => setOpen(false)}
|
onCancel={() => setOpen(false)}
|
||||||
onOk={onSave}
|
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) => (
|
{HOTKEY_FUNC.map((func) => (
|
||||||
<ItemWrapper key={func}>
|
<ItemWrapper key={func}>
|
||||||
<Typography>{t(func)}</Typography>
|
<Typography>{t(func)}</Typography>
|
||||||
|
@ -328,6 +328,7 @@
|
|||||||
"Default Latency Test Info": "仅用于 HTTP 客户端请求测试,不会对配置文件产生影响",
|
"Default Latency Test Info": "仅用于 HTTP 客户端请求测试,不会对配置文件产生影响",
|
||||||
"Default Latency Timeout": "测试超时时间",
|
"Default Latency Timeout": "测试超时时间",
|
||||||
"Hotkey Setting": "热键设置",
|
"Hotkey Setting": "热键设置",
|
||||||
|
"Enable Global Hotkey": "启用全局热键",
|
||||||
"open_or_close_dashboard": "打开/关闭面板",
|
"open_or_close_dashboard": "打开/关闭面板",
|
||||||
"clash_mode_rule": "规则模式",
|
"clash_mode_rule": "规则模式",
|
||||||
"clash_mode_global": "全局模式",
|
"clash_mode_global": "全局模式",
|
||||||
|
1
src/services/types.d.ts
vendored
1
src/services/types.d.ts
vendored
@ -711,6 +711,7 @@ interface IVergeConfig {
|
|||||||
enable_auto_launch?: boolean;
|
enable_auto_launch?: boolean;
|
||||||
enable_silent_start?: boolean;
|
enable_silent_start?: boolean;
|
||||||
enable_system_proxy?: boolean;
|
enable_system_proxy?: boolean;
|
||||||
|
enable_global_hotkey?: boolean;
|
||||||
proxy_auto_config?: boolean;
|
proxy_auto_config?: boolean;
|
||||||
pac_file_content?: string;
|
pac_file_content?: string;
|
||||||
enable_random_port?: boolean;
|
enable_random_port?: boolean;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user