Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 11 additions & 4 deletions Actions/ShowFloatingWindowAction.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,9 +35,9 @@ protected override async Task OnInvoke()
_logger.LogDebug("没有可用的悬浮窗组件,强制隐藏悬浮窗");
}

if (IsRevertable)
if (IsRevertable && config != null)
{
PreviousStates[ActionSet.Guid] = GlobalConstants.MainConfig!.Data.ShowFloatingWindow;
PreviousStates[ActionSet.Guid] = config.ShowFloatingWindow;
}

if (config != null)
Expand Down Expand Up @@ -70,8 +70,15 @@ protected override async Task OnRevert()
return;
}

GlobalConstants.MainConfig!.Data.ShowFloatingWindow = previousState;
GlobalConstants.MainConfig.Save();
var config = GlobalConstants.MainConfig;
if (config == null)
{
_logger.LogWarning("主配置为空,无法恢复悬浮窗状态");
return;
}

config.Data.ShowFloatingWindow = previousState;
config.Save();
_floatingWindowService.UpdateWindowState();

_logger.LogInformation("已恢复悬浮窗状态为: {State}", previousState ? "开启" : "关闭");
Expand Down
37 changes: 37 additions & 0 deletions Actions/SwitchFloatingWindowThemeAction.cs
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using ClassIsland.Core.Abstractions.Automation;
using ClassIsland.Core.Attributes;
using ClassIsland.Shared;
using Microsoft.Extensions.Logging;
using SystemTools.Services;
using SystemTools.Settings;
using SystemTools.Shared;

namespace SystemTools.Actions;

Expand All @@ -16,6 +18,7 @@ namespace SystemTools.Actions;
public class SwitchFloatingWindowThemeAction(ILogger<SwitchFloatingWindowThemeAction> logger) : ActionBase<SwitchFloatingWindowThemeSettings>
{
private readonly ILogger<SwitchFloatingWindowThemeAction> _logger = logger;
private static readonly ConcurrentDictionary<Guid, int> PreviousThemes = new();

protected override async Task OnInvoke()
{
Expand All @@ -24,14 +27,25 @@ protected override async Task OnInvoke()
try
{
var service = IAppHost.GetService<FloatingWindowService>();
var config = GlobalConstants.MainConfig?.Data;

if (Settings.TargetTheme >= 0)
{
if (IsRevertable && config != null)
{
PreviousThemes[ActionSet.Guid] = config.FloatingWindowTheme;
}

service.SetWindowTheme(Settings.TargetTheme);
_logger.LogInformation("已设置悬浮窗主题为: {Theme}", GetThemeName(Settings.TargetTheme));
}
else
{
if (IsRevertable && config != null)
{
PreviousThemes[ActionSet.Guid] = config.FloatingWindowTheme;
}

service.ToggleWindowTheme();
_logger.LogInformation("已切换到下一个悬浮窗主题");
}
Expand All @@ -46,6 +60,29 @@ protected override async Task OnInvoke()
_logger.LogDebug("SwitchFloatingWindowThemeAction OnInvoke 完成");
}

protected override async Task OnRevert()
{
await base.OnRevert();

if (!PreviousThemes.TryRemove(ActionSet.Guid, out var previousTheme))
{
_logger.LogInformation("未找到主题恢复快照,跳过悬浮窗主题恢复。ActionSet={ActionSetGuid}", ActionSet.Guid);
return;
}

try
{
var service = IAppHost.GetService<FloatingWindowService>();
service.SetWindowTheme(previousTheme);
_logger.LogInformation("已恢复悬浮窗主题为: {Theme}", GetThemeName(previousTheme));
}
catch (Exception ex)
{
_logger.LogError(ex, "恢复悬浮窗主题失败");
throw;
}
}

private static string GetThemeName(int theme)
{
return theme switch
Expand Down
40 changes: 38 additions & 2 deletions Actions/ToggleFloatingWindowLayerAction.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using ClassIsland.Core.Abstractions.Automation;
using ClassIsland.Core.Attributes;
Expand All @@ -16,6 +17,7 @@ namespace SystemTools.Actions;
public class ToggleFloatingWindowLayerAction(ILogger<ToggleFloatingWindowLayerAction> logger) : ActionBase<ToggleFloatingWindowLayerSettings>
{
private readonly ILogger<ToggleFloatingWindowLayerAction> _logger = logger;
private static readonly ConcurrentDictionary<Guid, int> PreviousLayers = new();

protected override async Task OnInvoke()
{
Expand All @@ -24,16 +26,27 @@ protected override async Task OnInvoke()
try
{
var service = IAppHost.GetService<FloatingWindowService>();
var profile = service.ProfileManager.CurrentProfile;

// 根据设置决定是切换还是设置到指定层级
// TargetLayer: -1=切换, 0=置顶, 1=置底
// TargetLayer: -1=切换, 0=置底, 1=置顶
if (Settings.TargetLayer >= 0)
{
if (IsRevertable)
{
PreviousLayers[ActionSet.Guid] = profile.FloatingWindowLayer;
}

service.SetWindowLayer(Settings.TargetLayer);
_logger.LogInformation("已设置悬浮窗层级为: {Layer}", Settings.TargetLayer == 0 ? "置顶" : "置底");
_logger.LogInformation("已设置悬浮窗层级为: {Layer}", Settings.TargetLayer == 0 ? "置底" : "置顶");
}
else
{
if (IsRevertable)
{
PreviousLayers[ActionSet.Guid] = profile.FloatingWindowLayer;
}

service.ToggleWindowLayer();
_logger.LogInformation("已切换悬浮窗层级状态");
}
Expand All @@ -47,4 +60,27 @@ protected override async Task OnInvoke()
await base.OnInvoke();
_logger.LogDebug("ToggleFloatingWindowLayerAction OnInvoke 完成");
}

protected override async Task OnRevert()
{
await base.OnRevert();

if (!PreviousLayers.TryRemove(ActionSet.Guid, out var previousLayer))
{
_logger.LogInformation("未找到层级恢复快照,跳过悬浮窗层级恢复。ActionSet={ActionSetGuid}", ActionSet.Guid);
return;
}

try
{
var service = IAppHost.GetService<FloatingWindowService>();
service.SetWindowLayer(previousLayer);
_logger.LogInformation("已恢复悬浮窗层级为: {Layer}", previousLayer == 0 ? "置底" : "置顶");
}
catch (Exception ex)
{
_logger.LogError(ex, "恢复悬浮窗层级失败");
throw;
}
}
}
36 changes: 36 additions & 0 deletions Actions/ToggleFloatingWindowProfileAction.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Collections.Concurrent;
using System.Threading.Tasks;
using ClassIsland.Core.Abstractions.Automation;
using ClassIsland.Core.Attributes;
Expand All @@ -16,6 +17,7 @@ namespace SystemTools.Actions;
public class ToggleFloatingWindowProfileAction(ILogger<ToggleFloatingWindowProfileAction> logger) : ActionBase<ToggleFloatingWindowProfileSettings>
{
private readonly ILogger<ToggleFloatingWindowProfileAction> _logger = logger;
private static readonly ConcurrentDictionary<Guid, string> PreviousProfiles = new();

protected override async Task OnInvoke()
{
Expand All @@ -24,16 +26,27 @@ protected override async Task OnInvoke()
try
{
var service = IAppHost.GetService<FloatingWindowService>();
var currentProfileName = service.ProfileManager.CurrentProfileName;

// 根据设置决定是切换到下一个还是切换到指定方案
// TargetProfileName: null=切换到下一个, 其他=指定方案名称
if (!string.IsNullOrWhiteSpace(Settings.TargetProfileName))
{
if (IsRevertable)
{
PreviousProfiles[ActionSet.Guid] = currentProfileName;
}

service.SwitchToProfile(Settings.TargetProfileName);
_logger.LogInformation("已切换到悬浮窗配置方案: {Name}", Settings.TargetProfileName);
}
else
{
if (IsRevertable)
{
PreviousProfiles[ActionSet.Guid] = currentProfileName;
}

service.ToggleWindowProfile();
_logger.LogInformation("已切换到下一个悬浮窗配置方案");
}
Expand All @@ -47,4 +60,27 @@ protected override async Task OnInvoke()
await base.OnInvoke();
_logger.LogDebug("ToggleFloatingWindowProfileAction OnInvoke 完成");
}

protected override async Task OnRevert()
{
await base.OnRevert();

if (!PreviousProfiles.TryRemove(ActionSet.Guid, out var previousProfile))
{
_logger.LogInformation("未找到配置方案恢复快照,跳过悬浮窗配置方案恢复。ActionSet={ActionSetGuid}", ActionSet.Guid);
return;
}

try
{
var service = IAppHost.GetService<FloatingWindowService>();
service.SwitchToProfile(previousProfile);
_logger.LogInformation("已恢复悬浮窗配置方案为: {Name}", previousProfile);
}
catch (Exception ex)
{
_logger.LogError(ex, "恢复悬浮窗配置方案失败");
throw;
}
}
}
6 changes: 3 additions & 3 deletions Controls/ToggleFloatingWindowLayerSettingsControl.cs
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,9 @@ public ToggleFloatingWindowLayerSettingsControl()
{
HorizontalAlignment = Avalonia.Layout.HorizontalAlignment.Stretch
};
_layerComboBox.Items.Add(new ComboBoxItem { Content = "切换(置顶↔置底)", Tag = -1 });
_layerComboBox.Items.Add(new ComboBoxItem { Content = "置顶", Tag = 0 });
_layerComboBox.Items.Add(new ComboBoxItem { Content = "置底", Tag = 1 });
_layerComboBox.Items.Add(new ComboBoxItem { Content = "切换(置底↔置顶)", Tag = -1 });
_layerComboBox.Items.Add(new ComboBoxItem { Content = "置底", Tag = 0 });
_layerComboBox.Items.Add(new ComboBoxItem { Content = "置顶", Tag = 1 });
_layerComboBox.SelectedIndex = 0;

panel.Children.Add(_layerComboBox);
Expand Down
84 changes: 73 additions & 11 deletions Services/FloatingWindowService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -432,6 +432,8 @@ private void OnRulesetStatusUpdated(object? sender, EventArgs e)
CheckFloatingWindowRuleset();
CheckButtonRulesets();
CheckRowRulesets();
// 规则集变化后,重新评估窗口显隐(避免"所有按钮都被隐藏但窗口还显示")
ApplyVisibility();
}

private void CheckFloatingWindowRuleset()
Expand Down Expand Up @@ -576,7 +578,8 @@ private void ApplyVisibility()
}

var profile = _profileManager.CurrentProfile;
var shouldShow = _configHandler.Data.ShowFloatingWindow && _entries.Count > 0 && !_rulesetHidingWindow;
var hasVisibleButtons = HasAnyVisibleButton();
var shouldShow = _configHandler.Data.ShowFloatingWindow && hasVisibleButtons && !_rulesetHidingWindow;

if (shouldShow)
{
Expand Down Expand Up @@ -660,16 +663,7 @@ private void RefreshWindowButtons()
_stackPanel.HorizontalAlignment = HorizontalAlignment.Center;

_stackPanel.Children.Clear();

if (_isTouchDeviceDetected)
{
_touchDragHandle = CreateTouchDragHandle(scale, contentForeground);
_stackPanel.Children.Add(_touchDragHandle);
}
else
{
_touchDragHandle = null;
}
_touchDragHandle = null;

int rowIndex = 0;
foreach (var rowEntries in GetOrderedRows())
Expand Down Expand Up @@ -789,6 +783,74 @@ private void RefreshWindowButtons()

rowIndex++;
}

// 仅在"至少有一个可见按钮"时才显示拖拽把手,避免孤零零一个把手
var hasVisibleButtons = _stackPanel.Children.Count > 0;
var showDragHandle = (_isTouchDeviceDetected || _profileManager.CurrentProfile.FloatingWindowDragHandleAlwaysVisible)
&& hasVisibleButtons;

if (showDragHandle)
{
_touchDragHandle = CreateTouchDragHandle(scale, contentForeground);
_stackPanel.Children.Insert(0, _touchDragHandle);
}
}

/// <summary>
/// 判断是否至少有 1 个按钮在"经过规则集过滤后"是可见的。
/// 用于避免悬浮窗在没有任何可见按钮时(被规则集全部隐藏)仍然显示。
/// </summary>
private bool HasAnyVisibleButton()
{
if (_entries.Count == 0)
{
return false;
}

var profile = _profileManager.CurrentProfile;
var rowConfigs = profile.FloatingWindowRowRulesets;
var hiddenRowSet = new HashSet<int>();

if (rowConfigs != null)
{
for (int i = 0; i < rowConfigs.Count; i++)
{
var cfg = rowConfigs[i];
var shouldHide = !cfg.IsVisible
|| (cfg.HideOnRule && cfg.HidingRules != null
&& IAppHost.TryGetService<IRulesetService>() is { } rs
&& rs.IsRulesetSatisfied(cfg.HidingRules));
if (shouldHide)
{
hiddenRowSet.Add(i);
}
}
}

int rowIndex = 0;
foreach (var row in profile.FloatingWindowButtonRows ?? [])
{
if (!hiddenRowSet.Contains(rowIndex))
{
foreach (var id in row)
{
if (_rulesetHiddenButtons.Contains(id))
{
continue;
}
foreach (var entry in _entries.Values)
{
if (string.Equals(entry.ButtonId, id, StringComparison.Ordinal))
{
return true;
}
}
}
}
rowIndex++;
}

return false;
}

private List<List<FloatingWindowEntry>> GetOrderedRows()
Expand Down
2 changes: 1 addition & 1 deletion Settings/ToggleFloatingWindowLayerSettings.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ namespace SystemTools.Settings;
public class ToggleFloatingWindowLayerSettings
{
/// <summary>
/// 目标层级。-1 表示切换,0 表示置顶,1 表示置底
/// 目标层级。-1 表示切换,0 表示置底,1 表示置顶
/// </summary>
[JsonPropertyName("targetLayer")]
public int TargetLayer { get; set; } = -1;
Expand Down
Loading
Loading