Skip to content
Open
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
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ .. RitsuShellThemeCatalog.RegisteredThemeIds
T("ritsulib.mainMenuScrolling.enabled.label", "Enable main menu scrolling"),
ui.MainMenuScrollingEnabled,
T("ritsulib.mainMenuScrolling.enabled.description",
"Keeps long menu lists within the screen and scrolls focused entries into view. Enabled by default. Disable to use the original menu layout and navigation. Restart the game after changing this setting."));
"Keeps long menu lists within the screen. Overflow shrinks and fades edge buttons; mouse wheel and focus still scroll. Enabled by default. Disable to use the original menu layout and navigation. Restart the game after changing this setting."));
}

#if !STS2_AT_LEAST_0_108_0
Expand Down
2 changes: 1 addition & 1 deletion src/Settings/Localization/ModSettingsUi/eng.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@
"ritsulib.mainMenuModSettingsButton.enabled.label": "Show main menu settings shortcut",
"ritsulib.mainMenuModSettingsButton.enabled.description": "Shows a RitsuLib settings shortcut under the patch notes button on the main menu.",
"ritsulib.mainMenuScrolling.enabled.label": "Enable main menu scrolling",
"ritsulib.mainMenuScrolling.enabled.description": "Keeps long menu lists within the screen and scrolls focused entries into view. Enabled by default. Disable to use the original menu layout and navigation. Restart the game after changing this setting.",
"ritsulib.mainMenuScrolling.enabled.description": "Keeps long menu lists within the screen. Overflow shrinks and fades edge buttons; mouse wheel and focus still scroll. Enabled by default. Disable to use the original menu layout and navigation. Restart the game after changing this setting.",
"ritsulib.modSourceHoverTips.enabled.label": "Show content source hover tips",
"ritsulib.modSourceHoverTips.enabled.description": "Shows which mod provides cards, relics, potions, events, keywords, and other model hover tips.",
"ritsulib.modSourceHoverTips.displayStyle.label": "Source display style",
Expand Down
2 changes: 1 addition & 1 deletion src/Settings/Localization/ModSettingsUi/zhs.json
Original file line number Diff line number Diff line change
Expand Up @@ -213,7 +213,7 @@
"ritsulib.mainMenuModSettingsButton.enabled.label": "显示主菜单设置快捷入口",
"ritsulib.mainMenuModSettingsButton.enabled.description": "在主菜单更新日志按钮下方显示 RitsuLib 设置快捷入口。",
"ritsulib.mainMenuScrolling.enabled.label": "启用主菜单滚动",
"ritsulib.mainMenuScrolling.enabled.description": "将过长的菜单列表限制在屏幕内,并自动滚动显示当前选中项。默认启用;关闭后使用原版菜单布局和导航。修改后请重启游戏。",
"ritsulib.mainMenuScrolling.enabled.description": "将过长的菜单列表限制在屏幕内,边缘按钮会变小变透明,表示还能滚动;滚轮和焦点仍可滚动。默认启用;关闭后使用原版菜单布局和导航。修改后请重启游戏。",
"ritsulib.modSourceHoverTips.enabled.label": "显示内容来源悬停提示",
"ritsulib.modSourceHoverTips.enabled.description": "为卡牌、遗物、药水、事件、关键词和其他模型悬停提示显示内容来源模组。",
"ritsulib.modSourceHoverTips.displayStyle.label": "来源显示样式",
Expand Down
88 changes: 84 additions & 4 deletions src/Ui/MainMenu/NMainMenuScroller.Decorations.cs
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,7 @@ internal bool AnimateRunInfo(NContinueRunInfo info, bool show)

private void ShowReticles(NMainMenuTextButton button)
{
var previous = _reticleButton;
_reticleButton = button;
_reticleTween?.Kill();
_reticleProgress = 0f;
Expand All @@ -70,6 +71,7 @@ private void ShowReticles(NMainMenuTextButton button)
UpdateDecorations();
}), 0f, 1f, 0.2).SetEase(Tween.EaseType.Out).SetTrans(Tween.TransitionType.Cubic);
UpdateDecorations();
RefreshEdgeScale(previous, button);
}

private void HideReticles(NMainMenuTextButton button)
Expand All @@ -79,14 +81,14 @@ private void HideReticles(NMainMenuTextButton button)
_reticleTween?.Kill();
_reticleButton = null;
UpdateDecorations();
RefreshEdgeScale(button);
}

private void UpdateDecorations()
{
if (!Initialized)
return;
var showReticles = IsActive && IsNavigable(_reticleButton) &&
_reticleButton!.GetParent() == this && IsRowFullyVisible(_reticleButton);
var showReticles = IsActive && IsReticleTarget(_reticleButton);
if (_reticleLeft != null && _reticleRight != null &&
IsInstanceValid(_reticleLeft) && IsInstanceValid(_reticleRight))
{
Expand Down Expand Up @@ -115,8 +117,9 @@ private void UpdateDecorations()
var position = _runInfoButton.GlobalPosition + _runInfoOffset + new Vector2(0f, -20f * _runInfoProgress);
position.X = Mathf.Clamp(position.X, menuRect.Position.X + EdgePadding,
Mathf.Max(menuRect.Position.X + EdgePadding, menuRect.End.X - _runInfo.Size.X - EdgePadding));
position.Y = Mathf.Clamp(position.Y, GlobalPosition.Y,
Mathf.Max(GlobalPosition.Y, menuRect.End.Y - _runInfo.Size.Y - EdgePadding));
var topLimit = ScrollEngaged ? GlobalPosition.Y : menuRect.Position.Y + EdgePadding;
position.Y = Mathf.Clamp(position.Y, topLimit,
Mathf.Max(topLimit, menuRect.End.Y - _runInfo.Size.Y - EdgePadding));
_runInfo.GlobalPosition = position;
var infoColor = _runInfo.Modulate;
infoColor.A = _runInfoProgress;
Expand All @@ -126,18 +129,95 @@ private void UpdateDecorations()
private void HideDecorations()
{
_reticleTween?.Kill();
var previous = _reticleButton;
_reticleButton = null;
_runInfoTween?.Kill();
_runInfoProgress = 0f;
if (_runInfo != null && IsInstanceValid(_runInfo))
RunInfoShown(_runInfo) = false;
UpdateDecorations();
RefreshEdgeScale(previous);
}

private void DisposeDecorations()
{
_reticleTween?.Kill();
_runInfoTween?.Kill();
}

private bool IsReticleTarget(Control? item)
{
return item != null && item == _reticleButton && IsNavigable(_reticleButton) &&
_reticleButton.GetParent() == this &&
(_reticleButton.HasFocus() || IsRowFullyVisible(_reticleButton));
}

private (float topWeight, float bottomWeight, float start) GetEdgeScaleContext()
{
var start = (ScrollEngaged
? ContentPadding
: (Size.Y - _contentHeight) / 2f) - _visualScroll;
return (Mathf.SmoothStep(0f, EdgeZone, _visualScroll),
Mathf.SmoothStep(0f, EdgeZone, _scrollLimit - _visualScroll),
start);
}

private void RefreshEdgeScale(params Control?[] items)
{
if (!Initialized)
return;
var (topWeight, bottomWeight, start) = GetEdgeScaleContext();
foreach (var item in items)
{
if (item == null || !IsInstanceValid(item) || !_measurements.TryGetValue(item, out var minimum))
continue;
ApplyEdgeScale(item, start + _rowTops[item], minimum.Y, topWeight, bottomWeight);
}
}

private void ApplyEdgeScale(Control item, float top, float height, float topWeight, float bottomWeight)
{
if (IsReticleTarget(item))

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

[P2] 悬停目标变化时也需要刷新边缘缩放。这里对当前目标恢复大小和透明度,但 ApplyEdgeScale 只由 PositionItems 调用;鼠标悬停路径中的 ShowReticles / HideReticles 及其动画只调用 UpdateDecorations,_Process 也不会在布局未变时重新定位。因此等待滚动动画结束,再将鼠标移入一个完全可见但处于 EdgeZone 内的缩小按钮,新的目标仍保持缩小、变淡,直到下一次滚动或布局刷新。反过来,在滚动期间已经恢复正常大小的目标,鼠标移走后也可能继续保留正常大小。请在目标切换或清除时重新计算受影响按钮的边缘缩放和透明度,覆盖无需再次滚动的鼠标移入、移出场景。

{
ResetEdgeScale(item);
return;
}

var amount = 1f;
var pivotY = height * 0.5f;
if (topWeight > 0f && top < EdgeZone)
BlendEdge(topWeight, top + height * 0.5f, height * 0.5f, height, ref amount, ref pivotY);
if (bottomWeight > 0f && top + height > Size.Y - EdgeZone)
BlendEdge(bottomWeight, Size.Y - (top + height * 0.5f), pivotY, 0f, ref amount, ref pivotY);

item.PivotOffset = new(item.Size.X * 0.5f, pivotY);
item.Scale = Vector2.One * Mathf.Lerp(EdgeMinScale, 1f, amount);
var color = item.Modulate;
color.A = Mathf.Lerp(EdgeMinAlpha, 1f, amount);
item.Modulate = color;
}

private static void BlendEdge(
float weight, float distance, float fromPivot, float toPivot, ref float amount, ref float pivotY)
{
var positional = SmootherStep01(distance / EdgeZone);
amount = Mathf.Min(amount, Mathf.Lerp(1f, positional, weight));
pivotY = Mathf.Lerp(fromPivot, toPivot, weight * (1f - positional));
}

private static float SmootherStep01(float t)
{
t = Mathf.Clamp(t, 0f, 1f);
return t * t * t * (t * (t * 6f - 15f) + 10f);
}

private void ResetEdgeScale(Control item)
{
item.Scale = Vector2.One;
item.PivotOffset = Vector2.Zero;
var color = item.Modulate;
color.A = 1f;
item.Modulate = color;
}
}
}
Loading