From 6083b216216f76e8e06612fb37b8f95d9908b296 Mon Sep 17 00:00:00 2001 From: "Michael Z. Kadaner" Date: Sat, 13 Jun 2026 13:50:37 -0400 Subject: [PATCH 1/3] Do not store `Annotation` in menu item. Retrieve it from the menu owner on demand. --- far/editor.cpp | 11 ++++++++++- far/vmenu.cpp | 32 +++++++++++++++++++------------- far/vmenu.hpp | 6 +++++- far/vmenu2.cpp | 1 - 4 files changed, 34 insertions(+), 16 deletions(-) diff --git a/far/editor.cpp b/far/editor.cpp index 2b9f6c1a83..eaf27f2cd6 100644 --- a/far/editor.cpp +++ b/far/editor.cpp @@ -3527,7 +3527,6 @@ namespace void add_item(FindCoord FoundCoords, string_view ItemText) { menu_item_ex Item{ string{ ItemText } }; - Item.Annotation.emplace(FoundCoords.Pos, segment::length_tag{ FoundCoords.SearchLen }); Item.ComplexUserData = FoundCoords; m_Menu->AddItem(Item); @@ -3606,6 +3605,16 @@ namespace return false; } ); + m_Menu->ListBox().RegisterItemAnnotationProvider( + [](const menu_item_ex& Item) + { + if (const auto* Coord{ std::any_cast(&Item.ComplexUserData) }) + { + return segment{ Coord->Pos, segment::length_tag{ Coord->SearchLen } }; + } + return segment{}; + } + ); } void toggle_zoom() diff --git a/far/vmenu.cpp b/far/vmenu.cpp index 3046de49a7..67b3c6bf6d 100644 --- a/far/vmenu.cpp +++ b/far/vmenu.cpp @@ -527,11 +527,6 @@ namespace return Item.Name; } - int safe_get_item_annotation(menu_item_ex const& Item) - { - return Item.Annotation.transform([](const auto Annotation) { return Annotation.start(); }).value_or(0); - } - std::pair item_hpos_limits(const int ItemLength, const int TextAreaWidth, const item_hscroll_policy Policy) noexcept { using enum item_hscroll_policy; @@ -949,7 +944,7 @@ int VMenu::AddItem(menu_item_ex&& NewItem,int PosAdd) const auto ItemLength{ GetItemVisualLength(NewMenuItem) }; UpdateMaxLength(ItemLength); - m_HorizontalTracker->add_item(NewMenuItem.HorizontalPosition, ItemLength, safe_get_item_annotation(NewMenuItem)); + m_HorizontalTracker->add_item(NewMenuItem.HorizontalPosition, ItemLength, SafeGetItemAnnotationStart(NewMenuItem)); const auto NewFlags = NewMenuItem.Flags; NewMenuItem.Flags = 0; @@ -967,13 +962,12 @@ bool VMenu::UpdateItem(const FarListUpdate *NewItem) auto& Item = Items[NewItem->Index]; m_HorizontalTracker->remove_item( - Item.HorizontalPosition, GetItemVisualLength(Item), safe_get_item_annotation(Item)); + Item.HorizontalPosition, GetItemVisualLength(Item), SafeGetItemAnnotationStart(Item)); // Освободим память... от ранее занятого ;-) if (NewItem->Item.Flags&LIF_DELETEUSERDATA) { Item.ComplexUserData = {}; - Item.Annotation.reset(); } Item.Name = NullToEmpty(NewItem->Item.Text); @@ -982,7 +976,7 @@ bool VMenu::UpdateItem(const FarListUpdate *NewItem) const auto ItemLength{ GetItemVisualLength(Item) }; UpdateMaxLength(ItemLength); - m_HorizontalTracker->add_item(Item.HorizontalPosition, ItemLength, safe_get_item_annotation(Item)); + m_HorizontalTracker->add_item(Item.HorizontalPosition, ItemLength, SafeGetItemAnnotationStart(Item)); SetMenuFlags(VMENU_UPDATEREQUIRED | (bFilterEnabled ? VMENU_REFILTERREQUIRED : VMENU_NONE)); @@ -1013,7 +1007,7 @@ int VMenu::DeleteItem(int ID, int Count) --ItemHiddenCount; m_HorizontalTracker->remove_item( - I.HorizontalPosition, GetItemVisualLength(I), safe_get_item_annotation(I)); + I.HorizontalPosition, GetItemVisualLength(I), SafeGetItemAnnotationStart(I)); } // а вот теперь перемещения @@ -2363,7 +2357,7 @@ bool VMenu::SetItemHPos(menu_item_ex& Item, const auto& GetNewHPos) return GetNewHPos(Item.HorizontalPosition, ItemLength); }(); - m_HorizontalTracker->update_item_hpos(Item.HorizontalPosition, NewHPos, ItemLength, safe_get_item_annotation(Item)); + m_HorizontalTracker->update_item_hpos(Item.HorizontalPosition, NewHPos, ItemLength, SafeGetItemAnnotationStart(Item)); if (Item.HorizontalPosition == NewHPos) return false; Item.HorizontalPosition = NewHPos; @@ -2457,7 +2451,7 @@ bool VMenu::AlignAnnotations() return SetAllItemsHPos( [&](const menu_item_ex& Item) { - return AlignPos - static_cast(visual_string_length(get_item_text(Item).substr(0, safe_get_item_annotation(Item)))); + return AlignPos - static_cast(visual_string_length(get_item_text(Item).substr(0, SafeGetItemAnnotationStart(Item)))); }); } @@ -2934,7 +2928,7 @@ std::tuple VMenu::GetItemTextWithHighlight(const menu_item_ex& const auto GetHighlight{ [&] { - if (Item.Annotation) return *Item.Annotation; + if (m_ItemAnnotationProvider) return m_ItemAnnotationProvider(Item); if (HotkeyPos != string::npos) return segment{ static_cast(HotkeyPos), segment::length_tag{ 1 } }; if (Item.AutoHotkey) return segment{ static_cast(Item.AutoHotkeyPos), segment::length_tag{ 1 } }; return segment{}; @@ -3427,6 +3421,11 @@ void VMenu::RegisterExtendedDataProvider(extended_item_data_getter&& ExtendedDat m_ExtendedDataSetter = std::move(ExtendedDataSetter); } +void VMenu::RegisterItemAnnotationProvider(item_annotation_provider&& ItemAnnotationProvider) +{ + m_ItemAnnotationProvider = std::move(ItemAnnotationProvider); +} + FarListItem *VMenu::MenuItem2FarList(const menu_item_ex *MItem, FarListItem *FItem) { if (FItem && MItem) @@ -3589,6 +3588,13 @@ int VMenu::GetItemVisualLength(const menu_item_ex& Item) const return static_cast(CheckFlags(VMENU_SHOWAMPERSAND) ? visual_string_length(ItemText) : HiStrlen(ItemText)); } +int VMenu::SafeGetItemAnnotationStart(const menu_item_ex& Item) const +{ + if (!m_ItemAnnotationProvider) return 0; + const auto Annotation{ m_ItemAnnotationProvider(Item) }; + return Annotation.empty() ? 0 : Annotation.start(); +} + #ifdef ENABLE_TESTS #include "testing.hpp" diff --git a/far/vmenu.hpp b/far/vmenu.hpp index 56aed92c01..c2bb3e645a 100644 --- a/far/vmenu.hpp +++ b/far/vmenu.hpp @@ -137,7 +137,6 @@ struct menu_item_ex: menu_item : menu_item{ std::forward(Name), Flags } {} - std::optional Annotation; std::any ComplexUserData; intptr_t SimpleUserData{}; @@ -251,6 +250,9 @@ class VMenu final: public Modal using extended_item_data_setter = std::function; void RegisterExtendedDataProvider(extended_item_data_getter&& ExtendedDataGetter, extended_item_data_setter&& ExtendedDataSetter); + using item_annotation_provider = std::function; + void RegisterItemAnnotationProvider(item_annotation_provider&& ItemAnnotationProvider); + int GetSelectPos() const { return SelectPos; } int GetLastSelectPosResult() const { return SelectPosResult; } int GetSelectPos(FarListPos *ListPos) const; @@ -324,6 +326,7 @@ class VMenu final: public Modal [[nodiscard]] int CalculateTextAreaWidth() const; [[nodiscard]] int GetItemVisualLength(const menu_item_ex& Item) const; + [[nodiscard]] int SafeGetItemAnnotationStart(const menu_item_ex& Item) const; int GetItemPosition(int Position) const; bool CheckKeyHiOrAcc(DWORD Key, int Type, bool Translate, bool ChangePos, int& NewPos); @@ -361,6 +364,7 @@ class VMenu final: public Modal fixed_column_provider m_FixedColumnProvider; extended_item_data_getter m_ExtendedDataGetter; extended_item_data_setter m_ExtendedDataSetter; + item_annotation_provider m_ItemAnnotationProvider; window_ptr CurrentWindow; bool PrevCursorVisible{}; size_t PrevCursorSize{}; diff --git a/far/vmenu2.cpp b/far/vmenu2.cpp index e8080fe862..8a9738d571 100644 --- a/far/vmenu2.cpp +++ b/far/vmenu2.cpp @@ -446,7 +446,6 @@ int VMenu2::AddItem(const menu_item_ex& NewItem, int PosAdd) auto& Item = at(PosAdd); Item.AccelKey=NewItem.AccelKey; - Item.Annotation = NewItem.Annotation; Resize(); return n; From 2d06495df94035c50c47099d9615c9db134b1d22 Mon Sep 17 00:00:00 2001 From: "Michael Z. Kadaner" Date: Sat, 13 Jun 2026 14:23:42 -0400 Subject: [PATCH 2/3] Added `segment::start_or` and `segment::end_or` --- far/common.tests.cpp | 4 ++++ far/common/segment.hpp | 14 +++++++++++--- far/vmenu.cpp | 4 +--- 3 files changed, 16 insertions(+), 6 deletions(-) diff --git a/far/common.tests.cpp b/far/common.tests.cpp index 400c998e78..4ad05cabc6 100644 --- a/far/common.tests.cpp +++ b/far/common.tests.cpp @@ -1258,6 +1258,10 @@ TEST_CASE("segment.constexpr") STATIC_REQUIRE(segment{ 5, segment::sentinel_tag{ 7 } }.start() == 5); STATIC_REQUIRE(segment{ 5, segment::sentinel_tag{ 7 } }.length() == 2); STATIC_REQUIRE(segment{ 5, segment::length_tag{ 2 } }.end() == 7); + STATIC_REQUIRE(segment{ 5, segment::sentinel_tag{ 7 } }.start_or(42) == 5); + STATIC_REQUIRE(segment{ 5, segment::length_tag{ 2 } }.end_or(42) == 7); + STATIC_REQUIRE(segment{}.start_or(42) == 42); + STATIC_REQUIRE(segment{}.end_or(42) == 42); STATIC_REQUIRE(segment{}.ray() == segment{ 0, segment::sentinel_tag{ std::numeric_limits::max() } }); STATIC_REQUIRE(segment{}.ray(42) == segment{ 42, segment::sentinel_tag{ std::numeric_limits::max() } }); STATIC_REQUIRE((segment{}.ray(42).iota() | std::views::drop(3) | std::views::take(3)).front() == 45); diff --git a/far/common/segment.hpp b/far/common/segment.hpp index dcfad4f325..a931a2405f 100644 --- a/far/common/segment.hpp +++ b/far/common/segment.hpp @@ -40,7 +40,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. //---------------------------------------------------------------------------- -template +template class segment_t { [[nodiscard]] @@ -77,6 +77,14 @@ class segment_t [[nodiscard]] constexpr T end() const noexcept { assert(!empty()); return m_End; } + template U> + [[nodiscard]] + constexpr T start_or(U default_value) const noexcept { return empty() ? default_value : start(); } + + template U> + [[nodiscard]] + constexpr T end_or(U default_value) const noexcept { return empty() ? default_value : end(); } + [[nodiscard]] constexpr auto iota() const noexcept { return empty() ? std::views::iota(T{}, T{}) : std::views::iota(start(), end()); } @@ -94,14 +102,14 @@ class segment_t : segment_t{ InitialPoint, length_tag{ domain_max() } }; } - template + template U> [[nodiscard]] static constexpr segment_t horizontal_extent(const rectangle_t& rect) noexcept { return { rect.left, length_tag{ rect.width() } }; } - template + template U> [[nodiscard]] static constexpr segment_t vertical_extent(const rectangle_t& rect) noexcept { diff --git a/far/vmenu.cpp b/far/vmenu.cpp index 67b3c6bf6d..135e06ecd3 100644 --- a/far/vmenu.cpp +++ b/far/vmenu.cpp @@ -3590,9 +3590,7 @@ int VMenu::GetItemVisualLength(const menu_item_ex& Item) const int VMenu::SafeGetItemAnnotationStart(const menu_item_ex& Item) const { - if (!m_ItemAnnotationProvider) return 0; - const auto Annotation{ m_ItemAnnotationProvider(Item) }; - return Annotation.empty() ? 0 : Annotation.start(); + return m_ItemAnnotationProvider ? m_ItemAnnotationProvider(Item).start_or(0) : 0; } #ifdef ENABLE_TESTS From 5121918a4b2f61e6b54a4a2c663004f28a7683ac Mon Sep 17 00:00:00 2001 From: "Michael Z. Kadaner" Date: Sat, 13 Jun 2026 14:27:25 -0400 Subject: [PATCH 3/3] Changelog and vbuild --- far/changelog | 8 ++++++++ far/vbuild.m4 | 2 +- 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/far/changelog b/far/changelog index 6d5aec68dc..3114e53693 100644 --- a/far/changelog +++ b/far/changelog @@ -1,3 +1,11 @@ +-------------------------------------------------------------------------------- +MZK 2026-06-14 09:12:38-04:00 - build 6701 + +1. Instead of storing Annotation in menu item, + retrieving it from the menu owner on demand. + +2. Refactoring. + -------------------------------------------------------------------------------- drkns 2026-06-14 10:28:08+01:00 - build 6700 diff --git a/far/vbuild.m4 b/far/vbuild.m4 index 8d802408b2..f3b67e383d 100644 --- a/far/vbuild.m4 +++ b/far/vbuild.m4 @@ -1 +1 @@ -6700 +6701