Razor panel for the game's hotkey rebinding screen. Lists hotkey definitions grouped by category, shows current and default bindings, and lets the player start a capture, clear one binding, restore one action, or reset all to defaults, with a two-step confirm on the reset and localized Russian/English text.
@using Sandbox
@using Sandbox.UI
@using Socer
@using System
@using System.Linq
@inherits Panel
<div class="hotkey-settings localized-text @GamePreferences.TextSizeClass">
<div class="hotkey-intro">
<label class="hotkey-title">@T("Управление под вас","Your controls")</label>
<label class="hotkey-help">@T("Выберите действие и нажмите новую клавишу или сочетание. Изменения сохраняются сразу.","Choose an action, then press a new key or key combination. Changes are saved immediately.")</label>
</div>
<div class="hotkey-guide">
<label>@T("Сочетания: Ctrl, Alt или Shift + клавиша.","Combinations: Ctrl, Alt or Shift + a key.")</label>
<label>@T("Во время назначения: Esc — отменить, Delete — убрать привязку.","While assigning: Esc cancels, Delete removes the binding.")</label>
<label>@T("Занятые сочетания не перезаписываются. Сначала освободите нужную клавишу.","Conflicting bindings are kept. Clear the occupied key before reusing it.")</label>
</div>
@if(GameHotkeys.Capturing)
{
<div class="hotkey-capture-banner">
<div><label>@T("Нажмите клавишу…","Press a key…")</label><small>@CapturingLabel()</small></div>
<button onclick=@Cancel>@T("Отменить","Cancel")</button>
</div>
}
@if(!string.IsNullOrEmpty(ActionMessage))
{
<label class="hotkey-message @(ActionFailed?"warning":"")">@ActionMessage</label>
}
else if(!string.IsNullOrEmpty(GameHotkeys.CaptureMessage))
{
<label class="hotkey-message @(GameHotkeys.Capturing?"warning":"")">@GameHotkeys.CaptureMessage</label>
}
<div class="hotkey-reset-bar">
<label class="hotkey-help">@T("Можно убрать любую привязку. Действия в меню останутся доступны мышью.","Any binding can be removed. Menu actions remain available with the mouse.")</label>
<button class="hotkey-reset-all" onclick=@ArmReset>@T("Сбросить все","Reset all")</button>
</div>
@if(ResetArmed)
{
<div class="hotkey-reset-confirm">
<label>@T("Вернуть исходные клавиши для всех действий? Ваши сочетания будут заменены.","Restore the default keys for every action? This replaces your custom bindings.")</label>
<div><button class="confirm" onclick=@ResetAll>@T("Да, сбросить","Restore defaults")</button><button onclick=@(()=>{ResetArmed=false;GameAudio.Tap();})>@T("Отмена","Cancel")</button></div>
</div>
}
@foreach(var group in GameHotkeys.Definitions.GroupBy(d=>d.Group(GamePreferences.Russian)))
{
<div class="hotkey-group">
<div class="hotkey-group-heading"><label>@group.Key</label><small>@group.Count()</small></div>
@foreach(var definition in group)
{
var action=definition;
bool listening=GameHotkeys.Capturing && GameHotkeys.CapturingId==action.Id;
bool unbound=string.IsNullOrEmpty(GameHotkeys.Binding(action.Id));
<div class="hotkey-row @(listening?"listening":"")">
<div class="hotkey-row-copy"><label class="hotkey-action">@action.Label(GamePreferences.Russian)</label><small>@T("По умолчанию: ","Default: ")@GameHotkeys.DisplayBinding(action.DefaultBinding)</small></div>
<div class="hotkey-row-controls">
<button class="hotkey-binding @(unbound?"unbound":"")" title="@T("Назначить клавишу или сочетание","Assign a key or combination")" onclick=@(()=>Begin(action.Id))>
<label>@(listening?T("Нажмите клавишу…","Press a key…"):GameHotkeys.Display(action.Id))</label><small>@(listening?"Esc":unbound?T("Назначить","Assign"):T("Изменить","Change"))</small>
</button>
<button class="hotkey-clear" disabled=@unbound title="@T("Убрать эту привязку","Remove this binding")" onclick=@(()=>Clear(action.Id))>@T("Убрать","Clear")</button>
<button class="hotkey-default" disabled="@(string.Equals(GameHotkeys.Binding(action.Id),action.DefaultBinding,StringComparison.OrdinalIgnoreCase))" title="@T("Вернуть исходную клавишу для этого действия","Restore this action's default key")" onclick=@(()=>ResetOne(action.Id,action.DefaultBinding))>@T("По умолчанию","Default")</button>
</div>
@if(listening)
{
<label class="hotkey-row-status">@(string.IsNullOrEmpty(GameHotkeys.CaptureMessage)?T("Esc — отменить · Delete — убрать привязку","Esc cancels · Delete removes the binding"):GameHotkeys.CaptureMessage)</label>
}
else if(ActionFailed && ActionId==action.Id)
{
<label class="hotkey-row-status">@ActionMessage</label>
}
</div>
}
</div>
}
</div>
@code {
bool ResetArmed,ActionFailed;
string ActionMessage="",ActionId="";
static string T(string ru,string en)=>GamePreferences.Russian?ru:en;
string CapturingLabel(){foreach(var definition in GameHotkeys.Definitions)if(definition.Id==GameHotkeys.CapturingId)return definition.Label(GamePreferences.Russian);return "";}
void Begin(string id){ResetArmed=false;ActionMessage=ActionId="";InputFocus.Clear();GameHotkeys.BeginCapture(id);GameAudio.Tap();}
void Cancel(){ActionMessage=ActionId="";GameHotkeys.CancelCapture();GameAudio.Tap();}
void Clear(string id){ResetArmed=false;ActionMessage=ActionId="";GameHotkeys.CancelCapture();GameHotkeys.ClearBinding(id);GameAudio.Tap();}
void ResetOne(string id,string binding)
{
ResetArmed=false;ActionId=id;GameHotkeys.CancelCapture();
ActionMessage=GameHotkeys.Bind(id,binding);ActionFailed=!string.IsNullOrEmpty(ActionMessage);GameAudio.Tap();
}
void ArmReset(){GameHotkeys.CancelCapture();ActionMessage=ActionId="";ResetArmed=true;GameAudio.Tap();}
void ResetAll()
{
GameHotkeys.CancelCapture();GameHotkeys.ResetDefaults();ResetArmed=false;ActionFailed=false;ActionId="";
ActionMessage="";GameAudio.Tap();
}
protected override int BuildHash()=>HashCode.Combine(GamePreferences.Revision,GameHotkeys.Revision,GameHotkeys.Capturing,
GameHotkeys.CapturingId,GameHotkeys.CaptureMessage,ResetArmed,HashCode.Combine(ActionMessage,ActionId,ActionFailed));
}