Repository :
https://github.com/FarGroup/FarManager
On branch : master
Link :
https://github.com/FarGroup/FarManager/commit/23a85cd2a9fce6f4e3da1c591a05faeeaff46f41
>---------------------------------------------------------------
commit 23a85cd2a9fce6f4e3da1c591a05faeeaff46f41
Author: Michael Z. Kadaner <
MKad...@users.noreply.github.com>
Date: Sat Jul 18 18:22:18 2026 -0400
Clarified `VMenu::AddHotkeys`
... and renamed it to `DecorateItemsWithHotkeys`
>---------------------------------------------------------------
23a85cd2a9fce6f4e3da1c591a05faeeaff46f41
far/changelog | 5 +++++
far/config.cpp | 10 +++++-----
far/filelist.cpp | 2 +-
far/vbuild.m4 | 2 +-
far/vmenu.cpp | 43 +++++++++++++++++++++++--------------------
far/vmenu.hpp | 2 +-
6 files changed, 36 insertions(+), 28 deletions(-)
diff --git a/far/changelog b/far/changelog
index 7761aaa42..baf45d8b0 100644
--- a/far/changelog
+++ b/far/changelog
@@ -1,3 +1,8 @@
+--------------------------------------------------------------------------------
+MZK 2026-07-18 21:23:19-04:00 - build 6722
+
+1. Clarified `VMenu::AddHotkeys` and renamed it to `DecorateItemsWithHotkeys`
+
--------------------------------------------------------------------------------
drkns 2026-07-18 15:20:22+01:00 - build 6721
diff --git a/far/config.cpp b/far/config.cpp
index 253d9ae24..5de5b2185 100644
--- a/far/config.cpp
+++ b/far/config.cpp
@@ -3118,7 +3118,7 @@ void Options::ShellOptions(bool LastCommand, const MOUSE_EVENT_RECORD *MouseEven
{ msg(lng::MMenuChangeDrive), 0, KEY_ALTF1 },
};
ApplyViewModesNames(LeftMenu);
- const auto LeftMenuStrings = VMenu::AddHotkeys(LeftMenu);
+ VMenu::DecorateItemsWithHotkeys(LeftMenu);
menu_item FilesMenu[]
{
@@ -3144,7 +3144,7 @@ void Options::ShellOptions(bool LastCommand, const MOUSE_EVENT_RECORD *MouseEven
{ msg(lng::MMenuInvertSelection), 0, KEY_MULTIPLY },
{ msg(lng::MMenuRestoreSelection), 0, KEY_CTRLM },
};
- const auto FilesMenuStrings = VMenu::AddHotkeys(FilesMenu);
+ VMenu::DecorateItemsWithHotkeys(FilesMenu);
menu_item CmdMenu[]
{
@@ -3169,7 +3169,7 @@ void Options::ShellOptions(bool LastCommand, const MOUSE_EVENT_RECORD *MouseEven
{ msg(lng::MMenuProcessList), 0, KEY_CTRLW },
{ msg(lng::MMenuHotPlugList), 0, 0 },
};
- const auto CmdMenuStrings = VMenu::AddHotkeys(CmdMenu);
+ VMenu::DecorateItemsWithHotkeys(CmdMenu);
menu_item OptionsMenu[]
{
@@ -3201,7 +3201,7 @@ void Options::ShellOptions(bool LastCommand, const MOUSE_EVENT_RECORD *MouseEven
{ string{}, LIF_SEPARATOR },
{ msg(lng::MMenuSaveSetup), 0, KEY_SHIFTF9 },
};
- const auto OptionsMenuStrings = VMenu::AddHotkeys(OptionsMenu);
+ VMenu::DecorateItemsWithHotkeys(OptionsMenu);
menu_item RightMenu[]
{
@@ -3227,7 +3227,7 @@ void Options::ShellOptions(bool LastCommand, const MOUSE_EVENT_RECORD *MouseEven
{ msg(lng::MMenuChangeDriveRight), 0, KEY_ALTF2 },
};
ApplyViewModesNames(RightMenu);
- const auto RightMenuStrings = VMenu::AddHotkeys(RightMenu);
+ VMenu::DecorateItemsWithHotkeys(RightMenu);
HMenuData MainMenu[]
diff --git a/far/filelist.cpp b/far/filelist.cpp
index c14bc3555..9c030193d 100644
--- a/far/filelist.cpp
+++ b/far/filelist.cpp
@@ -4967,7 +4967,7 @@ void FileList::SelectSortMode()
bool PlusPressed = false;
{
- const auto MenuStrings = VMenu::AddHotkeys(SortMenu);
+ VMenu::DecorateItemsWithHotkeys(SortMenu);
const auto SortModeMenu = VMenu2::create(msg(lng::MMenuSortTitle), SortMenu);
SortModeMenu->SetHelp(L"PanelCmdSort"sv);
diff --git a/far/vbuild.m4 b/far/vbuild.m4
index 1d5f5f6f9..560ba20f7 100644
--- a/far/vbuild.m4
+++ b/far/vbuild.m4
@@ -1 +1 @@
-6721
+6722
diff --git a/far/vmenu.cpp b/far/vmenu.cpp
index a5c2fe1c8..e0bd98b2a 100644
--- a/far/vmenu.cpp
+++ b/far/vmenu.cpp
@@ -522,11 +522,20 @@ namespace
return !(Item.Flags & (LIF_HIDDEN | LIF_FILTERED));
}
- string_view get_item_text(const menu_item_ex& Item)
+ string_view get_item_text(const menu_item& Item)
{
return Item.GetName();
}
+ int get_item_visual_length(const menu_item& Item, const bool ShowAmpersand)
+ {
+ if (Item.VisualLength != Item.InvalidVisualLength)
+ return Item.VisualLength;
+
+ const auto ItemText{ get_item_text(Item) };
+ return Item.VisualLength = static_cast<int>(ShowAmpersand ? visual_string_length(ItemText) : HiStrlen(ItemText));
+ }
+
std::pair<int, int> item_hpos_limits(const int ItemLength, const int TextAreaWidth, const item_hscroll_policy Policy) noexcept
{
using enum item_hscroll_policy;
@@ -3540,29 +3549,27 @@ const UUID& VMenu::Id() const
return MenuId;
}
-std::vector<string> VMenu::AddHotkeys(std::span<menu_item> const MenuItems)
+void VMenu::DecorateItemsWithHotkeys(std::span<menu_item> const MenuItems, const bool ShowAmpersand)
{
- // Does this function properly account for wide characters?
-
- std::vector<string> Result(MenuItems.size());
-
- const size_t MaxLength = std::ranges::fold_left(MenuItems, 0uz, [](size_t Value, const auto& i)
+ const auto MaxVisualLength = std::ranges::fold_left(MenuItems, 0, [ShowAmpersand](const auto Acc, const auto& Item)
{
- return std::max(Value, i.GetName().size());
+ return std::max(Acc, get_item_visual_length(Item, ShowAmpersand));
});
- for (const auto& [Item, Str]: zip(MenuItems, Result))
+ for (auto& Item : MenuItems)
{
if (Item.Flags & LIF_SEPARATOR || !Item.AccelKey)
continue;
- const auto Key = KeyToLocalizedText(Item.AccelKey);
- const auto Hl = HiStrlen(Item.GetName()) != visual_string_length(Item.GetName());
- Str = fit_to_left(Item.GetName(), MaxLength + (Hl? 2 : 1)) + Key;
- Item.SetName(Str);
+ // `fit_to_left` always preserves ampersand which occupies exactly one screen cell. In other words,
+ // it accounts for the same number of screen cell as calculated by `visual_string_length`.
+ // If we show ampersand, an item occupies same number of screen cells as `fit_to_left` accounted for.
+ // If we do not show ampersand and an item actually has ampersand, it will occupy one screen cell less than
+ // `fit_to_left` accounted for, so we need to add an extra space to compensate for disappeared ampersand.
+ const auto Hl{ !ShowAmpersand
+ && get_item_visual_length(Item, false) != static_cast<int>(visual_string_length(get_item_text(Item))) };
+ Item.SetName(fit_to_left(Item.GetName(), MaxVisualLength + (Hl? 2 : 1)) + KeyToLocalizedText(Item.AccelKey));
}
-
- return Result;
}
int VMenu::GetNaturalMenuWidth() const
@@ -3592,11 +3599,7 @@ int VMenu::CalculateTextAreaWidth() const
int VMenu::GetItemVisualLength(const menu_item_ex& Item) const
{
- if (Item.VisualLength != Item.InvalidVisualLength)
- return Item.VisualLength;
-
- const auto ItemText{ get_item_text(Item) };
- return Item.VisualLength = static_cast<int>(CheckFlags(VMENU_SHOWAMPERSAND) ? visual_string_length(ItemText) : HiStrlen(ItemText));
+ return get_item_visual_length(Item, CheckFlags(VMENU_SHOWAMPERSAND));
}
int VMenu::SafeGetItemAnnotationStart(const menu_item_ex& Item) const
diff --git a/far/vmenu.hpp b/far/vmenu.hpp
index 27c3850a1..44ce8fee2 100644
--- a/far/vmenu.hpp
+++ b/far/vmenu.hpp
@@ -310,7 +310,7 @@ public:
}
static FarListItem *MenuItem2FarList(const menu_item_ex *MItem, FarListItem *FItem);
- static std::vector<string> AddHotkeys(std::span<menu_item> MenuItems);
+ static void DecorateItemsWithHotkeys(std::span<menu_item> MenuItems, bool ShowAmpersand = false);
static bool ClickHandler(window* Menu, int MenuClick);
[[nodiscard]] int GetNaturalMenuWidth() const;