mirror of
https://github.com/MCCTeam/Minecraft-Console-Client
synced 2026-08-15 13:04:36 +00:00
Fix TUI book controls
Polish TUI book navigation and signed-book handling, update localized shortcut text, and document the final PageUp/PageDown interaction. Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
72001554a8
commit
12ab759f3b
5 changed files with 168 additions and 30 deletions
|
|
@ -93,7 +93,7 @@ namespace MinecraftClient.Commands
|
|||
private int OpenEditor(CmdResult r)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out _))
|
||||
if (!EnsureWritable(r, handler, out _, Translations.cmd_book_cannot_edit_signed))
|
||||
return -1;
|
||||
|
||||
return BookTuiHost.TryOpen(handler, BookHand.Main, editable: true)
|
||||
|
|
@ -104,7 +104,7 @@ namespace MinecraftClient.Commands
|
|||
private int WriteBook(CmdResult r, string text)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out _))
|
||||
if (!EnsureWritable(r, handler, out _, Translations.cmd_book_cannot_edit_signed))
|
||||
return -1;
|
||||
|
||||
IReadOnlyList<string> pages = SplitPages(text);
|
||||
|
|
@ -127,7 +127,7 @@ namespace MinecraftClient.Commands
|
|||
private int EditPage(CmdResult r, int page, string text)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out BookContent content))
|
||||
if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_cannot_edit_signed))
|
||||
return -1;
|
||||
|
||||
List<string> pages = content.Pages.ToList();
|
||||
|
|
@ -146,7 +146,7 @@ namespace MinecraftClient.Commands
|
|||
private int InsertPage(CmdResult r, int page, string text)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out BookContent content))
|
||||
if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_cannot_edit_signed))
|
||||
return -1;
|
||||
|
||||
List<string> pages = content.Pages.ToList();
|
||||
|
|
@ -165,7 +165,7 @@ namespace MinecraftClient.Commands
|
|||
private int DeletePage(CmdResult r, int page)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out BookContent content))
|
||||
if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_cannot_edit_signed))
|
||||
return -1;
|
||||
|
||||
List<string> pages = content.Pages.ToList();
|
||||
|
|
@ -187,7 +187,7 @@ namespace MinecraftClient.Commands
|
|||
private int SignBook(CmdResult r, string title)
|
||||
{
|
||||
McClient handler = CmdResult.currentHandler!;
|
||||
if (!EnsureWritable(r, handler, out BookContent content))
|
||||
if (!EnsureWritable(r, handler, out BookContent content, Translations.cmd_book_already_signed))
|
||||
return -1;
|
||||
|
||||
string normalizedTitle = title.Trim();
|
||||
|
|
@ -208,7 +208,7 @@ namespace MinecraftClient.Commands
|
|||
return false;
|
||||
}
|
||||
|
||||
private static bool EnsureWritable(CmdResult r, McClient handler, out BookContent content)
|
||||
private static bool EnsureWritable(CmdResult r, McClient handler, out BookContent content, string signedBookMessage)
|
||||
{
|
||||
content = BookContent.EmptyWritable;
|
||||
if (!EnsureInventory(r, handler))
|
||||
|
|
@ -217,13 +217,20 @@ namespace MinecraftClient.Commands
|
|||
Item? item = handler.GetHeldBook();
|
||||
if (!BookContentHelper.IsWritableBook(item))
|
||||
{
|
||||
r.SetAndReturn(CmdResult.Status.Fail, Translations.cmd_book_not_holding_writable);
|
||||
r.SetAndReturn(CmdResult.Status.Fail, GetWritableBookFailureMessage(item, signedBookMessage));
|
||||
return false;
|
||||
}
|
||||
|
||||
return BookContentHelper.TryRead(item, out content);
|
||||
}
|
||||
|
||||
private static string GetWritableBookFailureMessage(Item? item, string signedBookMessage)
|
||||
{
|
||||
return BookContentHelper.TryRead(item, out BookContent content) && content.IsSigned
|
||||
? signedBookMessage
|
||||
: Translations.cmd_book_not_holding_writable;
|
||||
}
|
||||
|
||||
private static IReadOnlyList<string> SplitPages(string text)
|
||||
{
|
||||
return BookContentHelper.NormalizePages(DecodeInlineText(text).Split(PageDelimiter));
|
||||
|
|
|
|||
|
|
@ -7584,6 +7584,14 @@ namespace MinecraftClient {
|
|||
get { return ResourceManager.GetString("cmd.book.not_holding_writable", resourceCulture); }
|
||||
}
|
||||
|
||||
internal static string cmd_book_already_signed {
|
||||
get { return ResourceManager.GetString("cmd.book.already_signed", resourceCulture); }
|
||||
}
|
||||
|
||||
internal static string cmd_book_cannot_edit_signed {
|
||||
get { return ResourceManager.GetString("cmd.book.cannot_edit_signed", resourceCulture); }
|
||||
}
|
||||
|
||||
internal static string cmd_book_tui_opened {
|
||||
get { return ResourceManager.GetString("cmd.book.tui_opened", resourceCulture); }
|
||||
}
|
||||
|
|
@ -7696,5 +7704,13 @@ namespace MinecraftClient {
|
|||
get { return ResourceManager.GetString("tui.book.reading", resourceCulture); }
|
||||
}
|
||||
|
||||
internal static string tui_book_edit_shortcut_tip {
|
||||
get { return ResourceManager.GetString("tui.book.edit_shortcut_tip", resourceCulture); }
|
||||
}
|
||||
|
||||
internal static string tui_book_page_shortcut_tip {
|
||||
get { return ResourceManager.GetString("tui.book.page_shortcut_tip", resourceCulture); }
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -2683,6 +2683,12 @@ see item details.</value>
|
|||
<data name="cmd.book.not_holding_writable" xml:space="preserve">
|
||||
<value>You must hold a writable book in your main hand.</value>
|
||||
</data>
|
||||
<data name="cmd.book.already_signed" xml:space="preserve">
|
||||
<value>Book already signed.</value>
|
||||
</data>
|
||||
<data name="cmd.book.cannot_edit_signed" xml:space="preserve">
|
||||
<value>You cannot edit a signed book.</value>
|
||||
</data>
|
||||
<data name="cmd.book.tui_opened" xml:space="preserve">
|
||||
<value>Book TUI opened.</value>
|
||||
</data>
|
||||
|
|
@ -2767,4 +2773,10 @@ see item details.</value>
|
|||
<data name="tui.book.reading" xml:space="preserve">
|
||||
<value>Reading book.</value>
|
||||
</data>
|
||||
<data name="tui.book.edit_shortcut_tip" xml:space="preserve">
|
||||
<value>PageUp/PageDown: previous/next page</value>
|
||||
</data>
|
||||
<data name="tui.book.page_shortcut_tip" xml:space="preserve">
|
||||
<value>PageUp/PageDown: previous/next page</value>
|
||||
</data>
|
||||
</root>
|
||||
|
|
|
|||
|
|
@ -4,6 +4,7 @@ using System.Linq;
|
|||
using Avalonia;
|
||||
using Avalonia.Controls;
|
||||
using Avalonia.Input;
|
||||
using Avalonia.Interactivity;
|
||||
using Avalonia.Layout;
|
||||
using Avalonia.Media;
|
||||
using Avalonia.Threading;
|
||||
|
|
@ -58,14 +59,23 @@ internal sealed class BookView : UserControl
|
|||
private readonly List<string> pages;
|
||||
private readonly TextBlock header;
|
||||
private readonly TextBlock status;
|
||||
private readonly TextBlock shortcutTip;
|
||||
private readonly TextBox pageText;
|
||||
private readonly TextBox titleText;
|
||||
private readonly Button previousButton;
|
||||
private readonly Button nextButton;
|
||||
private readonly Button insertButton;
|
||||
private readonly Button deleteButton;
|
||||
private readonly Button saveButton;
|
||||
private readonly Button signButton;
|
||||
private bool bookSigned;
|
||||
private int pageIndex;
|
||||
|
||||
public BookView(McClient handler, BookContent content, bool editable)
|
||||
{
|
||||
this.handler = handler;
|
||||
this.editable = editable;
|
||||
bookSigned = content.IsSigned;
|
||||
pages = content.Pages.ToList();
|
||||
if (pages.Count == 0)
|
||||
pages.Add(string.Empty);
|
||||
|
|
@ -87,11 +97,19 @@ internal sealed class BookView : UserControl
|
|||
TextWrapping = TextWrapping.Wrap
|
||||
};
|
||||
|
||||
shortcutTip = new TextBlock
|
||||
{
|
||||
Foreground = Brushes.DarkGray,
|
||||
Margin = new Thickness(1, 0),
|
||||
Text = Translations.tui_book_page_shortcut_tip,
|
||||
TextWrapping = TextWrapping.Wrap
|
||||
};
|
||||
|
||||
pageText = new TextBox
|
||||
{
|
||||
AcceptsReturn = true,
|
||||
TextWrapping = TextWrapping.Wrap,
|
||||
IsReadOnly = !editable,
|
||||
IsReadOnly = !CanEdit,
|
||||
Foreground = Brushes.White,
|
||||
Background = Brushes.Black,
|
||||
BorderBrush = Brushes.Gray,
|
||||
|
|
@ -100,20 +118,27 @@ internal sealed class BookView : UserControl
|
|||
};
|
||||
pageText.TextChanged += (_, _) =>
|
||||
{
|
||||
if (editable && pageIndex >= 0 && pageIndex < pages.Count)
|
||||
if (CanEdit && pageIndex >= 0 && pageIndex < pages.Count)
|
||||
pages[pageIndex] = pageText.Text ?? string.Empty;
|
||||
};
|
||||
|
||||
titleText = new TextBox
|
||||
{
|
||||
Watermark = Translations.tui_book_title_watermark,
|
||||
IsVisible = editable,
|
||||
IsVisible = CanEdit,
|
||||
Foreground = Brushes.White,
|
||||
Background = Brushes.Black,
|
||||
BorderBrush = Brushes.Gray,
|
||||
Margin = new Thickness(1)
|
||||
};
|
||||
|
||||
previousButton = Button(Translations.tui_book_prev, (_, _) => TryMovePage(-1));
|
||||
nextButton = Button(Translations.tui_book_next, (_, _) => TryMovePage(1));
|
||||
insertButton = Button(Translations.tui_book_insert, (_, _) => InsertPage(), editable);
|
||||
deleteButton = Button(Translations.tui_book_delete, (_, _) => DeletePage(), editable);
|
||||
saveButton = Button(Translations.tui_book_save, (_, _) => Save(), editable);
|
||||
signButton = Button(Translations.tui_book_sign, (_, _) => Sign(), editable);
|
||||
|
||||
var controls = new StackPanel
|
||||
{
|
||||
Orientation = Orientation.Horizontal,
|
||||
|
|
@ -121,12 +146,12 @@ internal sealed class BookView : UserControl
|
|||
Margin = new Thickness(1),
|
||||
Children =
|
||||
{
|
||||
Button(Translations.tui_book_prev, (_, _) => MovePage(-1)),
|
||||
Button(Translations.tui_book_next, (_, _) => MovePage(1)),
|
||||
Button(Translations.tui_book_insert, (_, _) => InsertPage(), editable),
|
||||
Button(Translations.tui_book_delete, (_, _) => DeletePage(), editable),
|
||||
Button(Translations.tui_book_save, (_, _) => Save(), editable),
|
||||
Button(Translations.tui_book_sign, (_, _) => Sign(), editable),
|
||||
previousButton,
|
||||
nextButton,
|
||||
insertButton,
|
||||
deleteButton,
|
||||
saveButton,
|
||||
signButton,
|
||||
Button(Translations.tui_book_close, (_, _) => Close())
|
||||
}
|
||||
};
|
||||
|
|
@ -138,6 +163,7 @@ internal sealed class BookView : UserControl
|
|||
{
|
||||
DockTo(header, Dock.Top),
|
||||
DockTo(status, Dock.Bottom),
|
||||
DockTo(shortcutTip, Dock.Bottom),
|
||||
DockTo(controls, Dock.Bottom),
|
||||
DockTo(titleText, Dock.Bottom),
|
||||
pageText
|
||||
|
|
@ -145,26 +171,31 @@ internal sealed class BookView : UserControl
|
|||
};
|
||||
|
||||
Content = panel;
|
||||
AttachedToVisualTree += (_, _) =>
|
||||
{
|
||||
AddHandler(KeyDownEvent, OnTunnelKeyDown, RoutingStrategies.Tunnel, handledEventsToo: true);
|
||||
FocusPageText();
|
||||
};
|
||||
DetachedFromVisualTree += (_, _) => RemoveHandler(KeyDownEvent, OnTunnelKeyDown);
|
||||
Refresh();
|
||||
}
|
||||
|
||||
protected override void OnKeyDown(KeyEventArgs e)
|
||||
private bool CanEdit => editable && !bookSigned;
|
||||
|
||||
private void OnTunnelKeyDown(object? sender, KeyEventArgs e)
|
||||
{
|
||||
if (e.Key == Key.PageUp)
|
||||
{
|
||||
MovePage(-1);
|
||||
TryMovePage(-1);
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Key == Key.PageDown)
|
||||
{
|
||||
MovePage(1);
|
||||
TryMovePage(1);
|
||||
e.Handled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
base.OnKeyDown(e);
|
||||
}
|
||||
|
||||
private static Control DockTo(Control control, Dock dock)
|
||||
|
|
@ -186,14 +217,22 @@ internal sealed class BookView : UserControl
|
|||
return button;
|
||||
}
|
||||
|
||||
private void MovePage(int delta)
|
||||
private bool TryMovePage(int delta)
|
||||
{
|
||||
pageIndex = Math.Clamp(pageIndex + delta, 0, pages.Count - 1);
|
||||
int targetPageIndex = Math.Clamp(pageIndex + delta, 0, pages.Count - 1);
|
||||
if (targetPageIndex == pageIndex)
|
||||
return false;
|
||||
|
||||
pageIndex = targetPageIndex;
|
||||
Refresh();
|
||||
return true;
|
||||
}
|
||||
|
||||
private void InsertPage()
|
||||
{
|
||||
if (!CanEdit)
|
||||
return;
|
||||
|
||||
pages.Insert(pageIndex + 1, string.Empty);
|
||||
pageIndex++;
|
||||
Refresh();
|
||||
|
|
@ -201,6 +240,9 @@ internal sealed class BookView : UserControl
|
|||
|
||||
private void DeletePage()
|
||||
{
|
||||
if (!CanEdit)
|
||||
return;
|
||||
|
||||
if (pages.Count == 1)
|
||||
pages[0] = string.Empty;
|
||||
else
|
||||
|
|
@ -213,6 +255,14 @@ internal sealed class BookView : UserControl
|
|||
|
||||
private void Save()
|
||||
{
|
||||
if (bookSigned || IsHeldBookSigned())
|
||||
{
|
||||
bookSigned = true;
|
||||
status.Text = Translations.cmd_book_cannot_edit_signed;
|
||||
RefreshEditability();
|
||||
return;
|
||||
}
|
||||
|
||||
if (!Validate(out string error))
|
||||
{
|
||||
status.Text = error;
|
||||
|
|
@ -226,6 +276,14 @@ internal sealed class BookView : UserControl
|
|||
|
||||
private void Sign()
|
||||
{
|
||||
if (bookSigned || IsHeldBookSigned())
|
||||
{
|
||||
bookSigned = true;
|
||||
status.Text = Translations.cmd_book_already_signed;
|
||||
RefreshEditability();
|
||||
return;
|
||||
}
|
||||
|
||||
string title = (titleText.Text ?? string.Empty).Trim();
|
||||
if (!Validate(out string error, title))
|
||||
{
|
||||
|
|
@ -233,8 +291,16 @@ internal sealed class BookView : UserControl
|
|||
return;
|
||||
}
|
||||
|
||||
status.Text = handler.SendBookEdit(pages, title)
|
||||
? Translations.tui_book_signed
|
||||
if (handler.SendBookEdit(pages, title))
|
||||
{
|
||||
bookSigned = true;
|
||||
status.Text = Translations.tui_book_signed;
|
||||
RefreshEditability();
|
||||
return;
|
||||
}
|
||||
|
||||
status.Text = IsHeldBookSigned()
|
||||
? Translations.cmd_book_already_signed
|
||||
: Translations.tui_book_save_failed;
|
||||
}
|
||||
|
||||
|
|
@ -274,9 +340,44 @@ internal sealed class BookView : UserControl
|
|||
|
||||
private void Refresh()
|
||||
{
|
||||
pageText.Text = pages[pageIndex];
|
||||
string currentPageText = pages[pageIndex];
|
||||
if (!string.Equals(pageText.Text, currentPageText, StringComparison.Ordinal))
|
||||
pageText.Text = currentPageText;
|
||||
|
||||
header.Text = string.Format(Translations.tui_book_page_header, pageIndex + 1, pages.Count);
|
||||
status.Text = editable ? Translations.tui_book_editing : Translations.tui_book_reading;
|
||||
pageText.Focus();
|
||||
status.Text = CanEdit ? Translations.tui_book_editing : Translations.tui_book_reading;
|
||||
RefreshEditability();
|
||||
FocusPageText();
|
||||
}
|
||||
|
||||
private void RefreshEditability()
|
||||
{
|
||||
previousButton.IsEnabled = pageIndex > 0;
|
||||
nextButton.IsEnabled = pageIndex < pages.Count - 1;
|
||||
insertButton.IsEnabled = CanEdit;
|
||||
deleteButton.IsEnabled = CanEdit;
|
||||
saveButton.IsEnabled = CanEdit;
|
||||
signButton.IsEnabled = CanEdit;
|
||||
pageText.IsReadOnly = !CanEdit;
|
||||
titleText.IsEnabled = CanEdit;
|
||||
titleText.IsVisible = CanEdit;
|
||||
shortcutTip.Text = CanEdit
|
||||
? Translations.tui_book_edit_shortcut_tip
|
||||
: Translations.tui_book_page_shortcut_tip;
|
||||
}
|
||||
|
||||
private void FocusPageText()
|
||||
{
|
||||
Dispatcher.UIThread.Post(() =>
|
||||
{
|
||||
pageText.Focus();
|
||||
if (CanEdit)
|
||||
pageText.CaretIndex = pageText.Text?.Length ?? 0;
|
||||
}, DispatcherPriority.Input);
|
||||
}
|
||||
|
||||
private bool IsHeldBookSigned()
|
||||
{
|
||||
return BookContentHelper.TryRead(handler.GetHeldBook(BookHand.Main), out BookContent content) && content.IsSigned;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue