mirror of
https://github.com/clash-verge-rev/clash-verge-rev.git
synced 2026-01-29 00:35:38 +08:00
feat: tray enhance (#5058)
* feat: proxy group sorting for tray * feat(tray): add inline proxy groups toggle
This commit is contained in:
@@ -198,6 +198,9 @@ pub struct IVerge {
|
|||||||
|
|
||||||
pub enable_tray_icon: Option<bool>,
|
pub enable_tray_icon: Option<bool>,
|
||||||
|
|
||||||
|
/// show proxy groups directly on tray root menu
|
||||||
|
pub tray_inline_proxy_groups: Option<bool>,
|
||||||
|
|
||||||
/// 自动进入轻量模式
|
/// 自动进入轻量模式
|
||||||
pub enable_auto_light_weight_mode: Option<bool>,
|
pub enable_auto_light_weight_mode: Option<bool>,
|
||||||
|
|
||||||
@@ -398,6 +401,7 @@ impl IVerge {
|
|||||||
webdav_password: None,
|
webdav_password: None,
|
||||||
enable_tray_speed: Some(false),
|
enable_tray_speed: Some(false),
|
||||||
enable_tray_icon: Some(true),
|
enable_tray_icon: Some(true),
|
||||||
|
tray_inline_proxy_groups: Some(false),
|
||||||
enable_global_hotkey: Some(true),
|
enable_global_hotkey: Some(true),
|
||||||
enable_auto_light_weight_mode: Some(false),
|
enable_auto_light_weight_mode: Some(false),
|
||||||
auto_light_weight_minutes: Some(10),
|
auto_light_weight_minutes: Some(10),
|
||||||
@@ -489,6 +493,7 @@ impl IVerge {
|
|||||||
patch!(webdav_password);
|
patch!(webdav_password);
|
||||||
patch!(enable_tray_speed);
|
patch!(enable_tray_speed);
|
||||||
patch!(enable_tray_icon);
|
patch!(enable_tray_icon);
|
||||||
|
patch!(tray_inline_proxy_groups);
|
||||||
patch!(enable_auto_light_weight_mode);
|
patch!(enable_auto_light_weight_mode);
|
||||||
patch!(auto_light_weight_minutes);
|
patch!(auto_light_weight_minutes);
|
||||||
patch!(enable_dns_settings);
|
patch!(enable_dns_settings);
|
||||||
@@ -585,6 +590,7 @@ pub struct IVergeResponse {
|
|||||||
pub webdav_password: Option<String>,
|
pub webdav_password: Option<String>,
|
||||||
pub enable_tray_speed: Option<bool>,
|
pub enable_tray_speed: Option<bool>,
|
||||||
pub enable_tray_icon: Option<bool>,
|
pub enable_tray_icon: Option<bool>,
|
||||||
|
pub tray_inline_proxy_groups: Option<bool>,
|
||||||
pub enable_auto_light_weight_mode: Option<bool>,
|
pub enable_auto_light_weight_mode: Option<bool>,
|
||||||
pub auto_light_weight_minutes: Option<u64>,
|
pub auto_light_weight_minutes: Option<u64>,
|
||||||
pub enable_dns_settings: Option<bool>,
|
pub enable_dns_settings: Option<bool>,
|
||||||
@@ -658,6 +664,7 @@ impl From<IVerge> for IVergeResponse {
|
|||||||
webdav_password: verge.webdav_password,
|
webdav_password: verge.webdav_password,
|
||||||
enable_tray_speed: verge.enable_tray_speed,
|
enable_tray_speed: verge.enable_tray_speed,
|
||||||
enable_tray_icon: verge.enable_tray_icon,
|
enable_tray_icon: verge.enable_tray_icon,
|
||||||
|
tray_inline_proxy_groups: verge.tray_inline_proxy_groups,
|
||||||
enable_auto_light_weight_mode: verge.enable_auto_light_weight_mode,
|
enable_auto_light_weight_mode: verge.enable_auto_light_weight_mode,
|
||||||
auto_light_weight_minutes: verge.auto_light_weight_minutes,
|
auto_light_weight_minutes: verge.auto_light_weight_minutes,
|
||||||
enable_dns_settings: verge.enable_dns_settings,
|
enable_dns_settings: verge.enable_dns_settings,
|
||||||
|
|||||||
@@ -605,11 +605,46 @@ async fn create_tray_menu(
|
|||||||
|
|
||||||
let proxy_nodes_data = handle::Handle::mihomo().await.get_proxies().await;
|
let proxy_nodes_data = handle::Handle::mihomo().await.get_proxies().await;
|
||||||
|
|
||||||
|
let runtime_proxy_groups_order = cmd::get_runtime_config()
|
||||||
|
.await
|
||||||
|
.map_err(|e| {
|
||||||
|
logging!(
|
||||||
|
error,
|
||||||
|
Type::Cmd,
|
||||||
|
"Failed to fetch runtime proxy groups for tray menu: {e}"
|
||||||
|
);
|
||||||
|
})
|
||||||
|
.ok()
|
||||||
|
.flatten()
|
||||||
|
.map(|config| {
|
||||||
|
config
|
||||||
|
.get("proxy-groups")
|
||||||
|
.and_then(|groups| groups.as_sequence())
|
||||||
|
.map(|groups| {
|
||||||
|
groups
|
||||||
|
.iter()
|
||||||
|
.filter_map(|group| group.get("name"))
|
||||||
|
.filter_map(|name| name.as_str())
|
||||||
|
.map(|name| name.to_string())
|
||||||
|
.collect::<Vec<String>>()
|
||||||
|
})
|
||||||
|
.unwrap_or_default()
|
||||||
|
});
|
||||||
|
|
||||||
|
let proxy_group_order_map = runtime_proxy_groups_order.as_ref().map(|group_names| {
|
||||||
|
group_names
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.map(|(index, name)| (name.clone(), index))
|
||||||
|
.collect::<HashMap<String, usize>>()
|
||||||
|
});
|
||||||
|
|
||||||
|
let verge_settings = Config::verge().await.latest_ref().clone();
|
||||||
|
let show_proxy_groups_inline = verge_settings.tray_inline_proxy_groups.unwrap_or(false);
|
||||||
|
|
||||||
let version = env!("CARGO_PKG_VERSION");
|
let version = env!("CARGO_PKG_VERSION");
|
||||||
|
|
||||||
let hotkeys = Config::verge()
|
let hotkeys = verge_settings
|
||||||
.await
|
|
||||||
.latest_ref()
|
|
||||||
.hotkeys
|
.hotkeys
|
||||||
.as_ref()
|
.as_ref()
|
||||||
.map(|h| {
|
.map(|h| {
|
||||||
@@ -653,8 +688,7 @@ async fn create_tray_menu(
|
|||||||
|
|
||||||
// 代理组子菜单
|
// 代理组子菜单
|
||||||
let proxy_submenus: Vec<Submenu<Wry>> = {
|
let proxy_submenus: Vec<Submenu<Wry>> = {
|
||||||
let mut submenus = Vec::new();
|
let mut submenus: Vec<(String, usize, Submenu<Wry>)> = Vec::new();
|
||||||
let mut group_name_submenus_hash = HashMap::new();
|
|
||||||
|
|
||||||
// TODO: 应用启动时,内核还未启动完全,无法获取代理节点信息
|
// TODO: 应用启动时,内核还未启动完全,无法获取代理节点信息
|
||||||
if let Ok(proxy_nodes_data) = proxy_nodes_data {
|
if let Ok(proxy_nodes_data) = proxy_nodes_data {
|
||||||
@@ -745,53 +779,32 @@ async fn create_tray_menu(
|
|||||||
true,
|
true,
|
||||||
&group_items_refs,
|
&group_items_refs,
|
||||||
) {
|
) {
|
||||||
group_name_submenus_hash.insert(group_name.to_string(), submenu);
|
let insertion_index = submenus.len();
|
||||||
|
submenus.push((group_name.to_string(), insertion_index, submenu));
|
||||||
} else {
|
} else {
|
||||||
log::warn!(target: "app", "创建代理组子菜单失败: {}", group_name);
|
log::warn!(target: "app", "创建代理组子菜单失败: {}", group_name);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 获取运行时代理组配置
|
if let Some(order_map) = proxy_group_order_map.as_ref() {
|
||||||
let runtime_proxy_groups_config = cmd::get_runtime_config()
|
submenus.sort_by(
|
||||||
.await
|
|(name_a, original_index_a, _), (name_b, original_index_b, _)| match (
|
||||||
.map_err(|e| {
|
order_map.get(name_a),
|
||||||
logging!(
|
order_map.get(name_b),
|
||||||
error,
|
) {
|
||||||
Type::Cmd,
|
(Some(index_a), Some(index_b)) => index_a.cmp(index_b),
|
||||||
"Failed to fetch runtime proxy groups for tray menu: {e}"
|
(Some(_), None) => std::cmp::Ordering::Less,
|
||||||
|
(None, Some(_)) => std::cmp::Ordering::Greater,
|
||||||
|
(None, None) => original_index_a.cmp(original_index_b),
|
||||||
|
},
|
||||||
);
|
);
|
||||||
})
|
|
||||||
.ok()
|
|
||||||
.flatten()
|
|
||||||
.map(|config| {
|
|
||||||
config
|
|
||||||
.get("proxy-groups")
|
|
||||||
.and_then(|groups| groups.as_sequence())
|
|
||||||
.map(|groups| {
|
|
||||||
groups
|
|
||||||
.iter()
|
|
||||||
.filter_map(|group| group.get("name"))
|
|
||||||
.filter_map(|name| name.as_str())
|
|
||||||
.map(|name| name.into())
|
|
||||||
.collect::<Vec<String>>()
|
|
||||||
})
|
|
||||||
.unwrap_or_default()
|
|
||||||
});
|
|
||||||
|
|
||||||
if let Some(runtime_proxy_groups_config) = runtime_proxy_groups_config {
|
|
||||||
for group_name in runtime_proxy_groups_config {
|
|
||||||
if let Some(submenu) = group_name_submenus_hash.get(&group_name) {
|
|
||||||
submenus.push(submenu.clone());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
for (_, submenu) in group_name_submenus_hash {
|
|
||||||
submenus.push(submenu);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
submenus
|
submenus
|
||||||
|
.into_iter()
|
||||||
|
.map(|(_, _, submenu)| submenu)
|
||||||
|
.collect()
|
||||||
};
|
};
|
||||||
|
|
||||||
// Pre-fetch all localized strings
|
// Pre-fetch all localized strings
|
||||||
@@ -865,21 +878,33 @@ async fn create_tray_menu(
|
|||||||
)?;
|
)?;
|
||||||
|
|
||||||
// 创建代理主菜单
|
// 创建代理主菜单
|
||||||
let proxies_submenu = if !proxy_submenus.is_empty() {
|
let (proxies_submenu, inline_proxy_items): (Option<Submenu<Wry>>, Vec<&dyn IsMenuItem<Wry>>) =
|
||||||
|
if show_proxy_groups_inline {
|
||||||
|
(
|
||||||
|
None,
|
||||||
|
proxy_submenus
|
||||||
|
.iter()
|
||||||
|
.map(|submenu| submenu as &dyn IsMenuItem<Wry>)
|
||||||
|
.collect(),
|
||||||
|
)
|
||||||
|
} else if !proxy_submenus.is_empty() {
|
||||||
let proxy_submenu_refs: Vec<&dyn IsMenuItem<Wry>> = proxy_submenus
|
let proxy_submenu_refs: Vec<&dyn IsMenuItem<Wry>> = proxy_submenus
|
||||||
.iter()
|
.iter()
|
||||||
.map(|submenu| submenu as &dyn IsMenuItem<Wry>)
|
.map(|submenu| submenu as &dyn IsMenuItem<Wry>)
|
||||||
.collect();
|
.collect();
|
||||||
|
|
||||||
|
(
|
||||||
Some(Submenu::with_id_and_items(
|
Some(Submenu::with_id_and_items(
|
||||||
app_handle,
|
app_handle,
|
||||||
"proxies",
|
"proxies",
|
||||||
proxies_text,
|
proxies_text,
|
||||||
true,
|
true,
|
||||||
&proxy_submenu_refs,
|
&proxy_submenu_refs,
|
||||||
)?)
|
)?),
|
||||||
|
Vec::new(),
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
None
|
(None, Vec::new())
|
||||||
};
|
};
|
||||||
|
|
||||||
let system_proxy = &CheckMenuItem::with_id(
|
let system_proxy = &CheckMenuItem::with_id(
|
||||||
@@ -991,7 +1016,11 @@ async fn create_tray_menu(
|
|||||||
];
|
];
|
||||||
|
|
||||||
// 如果有代理节点,添加代理节点菜单
|
// 如果有代理节点,添加代理节点菜单
|
||||||
if let Some(ref proxies_menu) = proxies_submenu {
|
if show_proxy_groups_inline {
|
||||||
|
if !inline_proxy_items.is_empty() {
|
||||||
|
menu_items.extend_from_slice(&inline_proxy_items);
|
||||||
|
}
|
||||||
|
} else if let Some(ref proxies_menu) = proxies_submenu {
|
||||||
menu_items.push(proxies_menu);
|
menu_items.push(proxies_menu);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -22,6 +22,7 @@ import { useWindowDecorations } from "@/hooks/use-window";
|
|||||||
import { copyIconFile, getAppDir } from "@/services/cmds";
|
import { copyIconFile, getAppDir } from "@/services/cmds";
|
||||||
import { showNotice } from "@/services/noticeService";
|
import { showNotice } from "@/services/noticeService";
|
||||||
import getSystem from "@/utils/get-system";
|
import getSystem from "@/utils/get-system";
|
||||||
|
|
||||||
import { GuardState } from "./guard-state";
|
import { GuardState } from "./guard-state";
|
||||||
|
|
||||||
const OS = getSystem();
|
const OS = getSystem();
|
||||||
@@ -263,6 +264,19 @@ export const LayoutViewer = forwardRef<DialogRef>((props, ref) => {
|
|||||||
</GuardState>
|
</GuardState>
|
||||||
</Item>
|
</Item>
|
||||||
)}
|
)}
|
||||||
|
<Item>
|
||||||
|
<ListItemText primary={t("Show Proxy Groups Inline")} />
|
||||||
|
<GuardState
|
||||||
|
value={verge?.tray_inline_proxy_groups ?? false}
|
||||||
|
valueProps="checked"
|
||||||
|
onCatch={onError}
|
||||||
|
onFormat={onSwitchFormat}
|
||||||
|
onChange={(e) => onChangeData({ tray_inline_proxy_groups: e })}
|
||||||
|
onGuard={(e) => patchVerge({ tray_inline_proxy_groups: e })}
|
||||||
|
>
|
||||||
|
<Switch edge="end" />
|
||||||
|
</GuardState>
|
||||||
|
</Item>
|
||||||
|
|
||||||
<Item>
|
<Item>
|
||||||
<ListItemText primary={t("Common Tray Icon")} />
|
<ListItemText primary={t("Common Tray Icon")} />
|
||||||
|
|||||||
@@ -512,6 +512,7 @@
|
|||||||
"Direct Mode": "Direct Mode",
|
"Direct Mode": "Direct Mode",
|
||||||
"Enable Tray Speed": "Enable Tray Speed",
|
"Enable Tray Speed": "Enable Tray Speed",
|
||||||
"Enable Tray Icon": "Enable Tray Icon",
|
"Enable Tray Icon": "Enable Tray Icon",
|
||||||
|
"Show Proxy Groups Inline": "Show Proxy Groups Inline",
|
||||||
"LightWeight Mode": "Lightweight Mode",
|
"LightWeight Mode": "Lightweight Mode",
|
||||||
"LightWeight Mode Info": "Close the GUI and keep only the kernel running",
|
"LightWeight Mode Info": "Close the GUI and keep only the kernel running",
|
||||||
"LightWeight Mode Settings": "LightWeight Mode Settings",
|
"LightWeight Mode Settings": "LightWeight Mode Settings",
|
||||||
|
|||||||
@@ -512,6 +512,7 @@
|
|||||||
"Direct Mode": "直连模式",
|
"Direct Mode": "直连模式",
|
||||||
"Enable Tray Speed": "启用托盘速率",
|
"Enable Tray Speed": "启用托盘速率",
|
||||||
"Enable Tray Icon": "启用托盘图标",
|
"Enable Tray Icon": "启用托盘图标",
|
||||||
|
"Show Proxy Groups Inline": "将代理组显示在托盘一级菜单",
|
||||||
"LightWeight Mode": "轻量模式",
|
"LightWeight Mode": "轻量模式",
|
||||||
"LightWeight Mode Info": "关闭GUI界面,仅保留内核运行",
|
"LightWeight Mode Info": "关闭GUI界面,仅保留内核运行",
|
||||||
"LightWeight Mode Settings": "轻量模式设置",
|
"LightWeight Mode Settings": "轻量模式设置",
|
||||||
|
|||||||
@@ -437,6 +437,7 @@
|
|||||||
"Direct Mode": "直連模式",
|
"Direct Mode": "直連模式",
|
||||||
"Enable Tray Speed": "啟用托盤速率",
|
"Enable Tray Speed": "啟用托盤速率",
|
||||||
"Enable Tray Icon": "啟用托盤圖標",
|
"Enable Tray Icon": "啟用托盤圖標",
|
||||||
|
"Show Proxy Groups Inline": "將代理組顯示在托盤一級選單",
|
||||||
"LightWeight Mode": "輕量模式",
|
"LightWeight Mode": "輕量模式",
|
||||||
"LightWeight Mode Info": "關閉GUI界面,僅保留內核運行",
|
"LightWeight Mode Info": "關閉GUI界面,僅保留內核運行",
|
||||||
"LightWeight Mode Settings": "輕量模式設置",
|
"LightWeight Mode Settings": "輕量模式設置",
|
||||||
|
|||||||
1
src/services/types.d.ts
vendored
1
src/services/types.d.ts
vendored
@@ -802,6 +802,7 @@ interface IVergeConfig {
|
|||||||
tun_tray_icon?: boolean;
|
tun_tray_icon?: boolean;
|
||||||
enable_tray_speed?: boolean;
|
enable_tray_speed?: boolean;
|
||||||
enable_tray_icon?: boolean;
|
enable_tray_icon?: boolean;
|
||||||
|
tray_inline_proxy_groups?: boolean;
|
||||||
enable_tun_mode?: boolean;
|
enable_tun_mode?: boolean;
|
||||||
enable_auto_light_weight_mode?: boolean;
|
enable_auto_light_weight_mode?: boolean;
|
||||||
auto_light_weight_minutes?: number;
|
auto_light_weight_minutes?: number;
|
||||||
|
|||||||
Reference in New Issue
Block a user