diff --git a/far/vmenu.cpp b/far/vmenu.cpp index 060f012351..17f7972aae 100644 --- a/far/vmenu.cpp +++ b/far/vmenu.cpp @@ -181,7 +181,6 @@ struct menu_layout // Must be in the TU scope because it is forward-declared in VMenu.hpp enum class item_hscroll_policy { - unbound, cling_to_edge, bound, bound_stick_to_left @@ -292,18 +291,13 @@ namespace return !(Item.Flags & (LIF_HIDDEN | LIF_FILTERED)); } - std::pair item_hpos_limits(const item_hscroll_policy Policy, const int ItemLength, const int MaxItemLength, const int TextAreaWidth) noexcept + std::pair item_hpos_limits(const item_hscroll_policy Policy, const int ItemLength, const int TextAreaWidth) noexcept { assert(ItemLength > 0); - assert(MaxItemLength > 0); assert(TextAreaWidth > 0); - assert(ItemLength <= MaxItemLength); switch (Policy) { - case item_hscroll_policy::unbound: - return{ 1 - MaxItemLength, TextAreaWidth - 1 + (MaxItemLength - ItemLength) }; - case item_hscroll_policy::cling_to_edge: return{ 1 - ItemLength, TextAreaWidth - 1 }; @@ -380,6 +374,25 @@ namespace return Shift; } + bool set_item_hpos(MenuItemEx& Item, std::regular_invocable auto const GetNewHPos, const bool ShowAmpersand, const int TextAreaWidth, const item_hscroll_policy Policy) + { + const auto ItemLength{ static_cast(ShowAmpersand ? visual_string_length(Item.Name) : HiStrlen(Item.Name)) }; + + // TBD: (TextAreaWidth <= 0) not needed? + if (ItemLength <= 0 || TextAreaWidth <= 0) return false; + + if (Item.Flags & LIF_SEPARATOR) return false; + + const auto HPosLimits{ item_hpos_limits(Policy, ItemLength, TextAreaWidth) }; + const auto ClampedHPos = std::clamp(GetNewHPos(ItemLength), HPosLimits.first, HPosLimits.second); + + if (Item.HPos == ClampedHPos) + return false; + + Item.HPos = ClampedHPos; + return true; + } + // Indices in the color array enum class color_indices { @@ -2131,6 +2144,7 @@ int VMenu::VisualPosToReal(int VPos) const /* +* TBD: Remove HScrollEnBlocMode ? item_hscroll_policy::unbound : CheckFlags(VMENU_ENABLEALIGNANNOTATIONS) ? item_hscroll_policy::cling_to_edge : item_hscroll_policy::bound_stick_to_left)) @@ -2151,10 +2165,15 @@ bool VMenu::SetAllItemsSmartHPos(const int NewHPos) for (auto& Item : Items) { - if (Item.Flags & LIF_SEPARATOR) continue; - - if (SetItemSmartHPos(Item, NewHPos, TextAreaWidth, Policy)) + if (set_item_hpos( + Item, + [=](int ItemLength) { return NewHPos >= 0 ? NewHPos : TextAreaWidth - ItemLength + NewHPos + 1; }, + CheckFlags(VMENU_SHOWAMPERSAND), + TextAreaWidth, + Policy)) + { NeedRedraw = true; + } } if (NeedRedraw) SetMenuFlags(VMENU_UPDATEREQUIRED); @@ -2169,9 +2188,16 @@ bool VMenu::SetCurItemSmartHPos(const int NewHPos) const auto Policy{ CheckFlags(VMENU_ENABLEALIGNANNOTATIONS) ? item_hscroll_policy::cling_to_edge : item_hscroll_policy::bound_stick_to_left }; auto& Item{ Items[SelectPos] }; - if (Item.Flags & LIF_SEPARATOR) return false; - if (!SetItemSmartHPos(Item, NewHPos, TextAreaWidth, Policy)) return false; + if (!set_item_hpos( + Item, + [=](int ItemLength) { return NewHPos >= 0 ? NewHPos : TextAreaWidth - ItemLength + NewHPos + 1; }, + CheckFlags(VMENU_SHOWAMPERSAND), + TextAreaWidth, + Policy)) + { + return false; + } SetMenuFlags(VMENU_UPDATEREQUIRED); return true; @@ -2197,9 +2223,9 @@ bool VMenu::ShiftCurItemHPos(const int Shift) const auto Policy{ CheckFlags(VMENU_ENABLEALIGNANNOTATIONS) ? item_hscroll_policy::cling_to_edge : item_hscroll_policy::bound_stick_to_left }; auto& Item{ Items[SelectPos] }; - if (Item.Flags & LIF_SEPARATOR) return false; - if (!SetItemAbsoluteHPos(Item, Item.HPos + Shift, TextAreaWidth, Policy)) return false; + if (!set_item_hpos(Item, [&](int) { return Item.HPos + Shift; }, CheckFlags(VMENU_SHOWAMPERSAND), TextAreaWidth, Policy)) + return false; SetMenuFlags(VMENU_UPDATEREQUIRED); return true; @@ -2277,57 +2303,58 @@ bool VMenu::ShiftAllItemsHPosLimited(const int Shift, const int TextAreaWidth) for (auto& Item : Items) { - if (Item.Flags & LIF_SEPARATOR) continue; - - if (SetItemAbsoluteHPos(Item, Item.HPos + Shift, TextAreaWidth, Policy)) + if (set_item_hpos(Item, [&](int) { return Item.HPos + Shift; }, CheckFlags(VMENU_SHOWAMPERSAND), TextAreaWidth, Policy)) NeedRedraw = true; } return NeedRedraw; } -bool VMenu::SetItemAbsoluteHPos(MenuItemEx& Item, const int NewHPos, const int ItemLength, const int TextAreaWidth, const item_hscroll_policy Policy) -{ - // TBD: (TextAreaWidth <= 0) not needed? - if (ItemLength <= 0 || TextAreaWidth <= 0) return false; - - const auto HPosLimits{ item_hpos_limits(Policy, ItemLength, static_cast(m_MaxItemLength), TextAreaWidth) }; - const auto ClampedHPos = std::clamp(NewHPos, HPosLimits.first, HPosLimits.second); - - if (Item.HPos == ClampedHPos) - return false; - - Item.HPos = ClampedHPos; - return true; -} - -bool VMenu::SetItemAbsoluteHPos(MenuItemEx& Item, const int NewHPos, const int TextAreaWidth, const item_hscroll_policy Policy) -{ - const auto ItemLength{ static_cast(CheckFlags(VMENU_SHOWAMPERSAND) ? visual_string_length(Item.Name) : HiStrlen(Item.Name)) }; - return SetItemAbsoluteHPos(Item, NewHPos, ItemLength, TextAreaWidth, Policy); -} - -bool VMenu::SetItemSmartHPos(MenuItemEx& Item, const int NewHPos, const int TextAreaWidth, const item_hscroll_policy Policy) -{ - const auto ItemLength{ static_cast(CheckFlags(VMENU_SHOWAMPERSAND) ? visual_string_length(Item.Name) : HiStrlen(Item.Name)) }; - return SetItemAbsoluteHPos(Item, NewHPos >= 0 ? NewHPos : TextAreaWidth - ItemLength + NewHPos + 1, ItemLength, TextAreaWidth, Policy); -} - -bool VMenu::ShiftItemHPos(MenuItemEx& Item, const int Shift, const int TextAreaWidth, const item_hscroll_policy Policy) -{ - return SetItemAbsoluteHPos(Item, Item.HPos + Shift, TextAreaWidth, Policy); -} - -bool VMenu::ShiftAllItemsHPos(const int Shift, const item_hscroll_policy Policy) -{ - const auto TextAreaWidth{ CalculateTextAreaWidth() }; - - return std::accumulate(Items.begin(), Items.end(), false, [&](const bool NeedRedraw, MenuItemEx& Item) - { - return ShiftItemHPos(Item, Shift, TextAreaWidth, Policy) - || NeedRedraw; - }); -} +// TBD: Remove +//bool VMenu::SetItemAbsoluteHPos(MenuItemEx& Item, const int NewHPos, const int ItemLength, const int TextAreaWidth, const item_hscroll_policy Policy) +//{ +// // TBD: (TextAreaWidth <= 0) not needed? +// if (ItemLength <= 0 || TextAreaWidth <= 0) return false; +// +// if (Item.Flags & LIF_SEPARATOR) return false; +// +// const auto HPosLimits{ item_hpos_limits(Policy, ItemLength, TextAreaWidth) }; +// const auto ClampedHPos = std::clamp(NewHPos, HPosLimits.first, HPosLimits.second); +// +// if (Item.HPos == ClampedHPos) +// return false; +// +// Item.HPos = ClampedHPos; +// return true; +//} + +//bool VMenu::SetItemAbsoluteHPos(MenuItemEx& Item, const int NewHPos, const int TextAreaWidth, const item_hscroll_policy Policy) +//{ +// return set_item_hpos(Item, [=](int) { return NewHPos; }, CheckFlags(VMENU_SHOWAMPERSAND), TextAreaWidth, Policy); +// //return set_item_hpos(Item, NewHPos, CheckFlags(VMENU_SHOWAMPERSAND), TextAreaWidth, Policy); +//} + +//bool VMenu::SetItemSmartHPos(MenuItemEx& Item, const int NewHPos, const int TextAreaWidth, const item_hscroll_policy Policy) +//{ +// return set_item_hpos(Item, [=](int ItemLength) { return NewHPos >= 0 ? NewHPos : TextAreaWidth - ItemLength + NewHPos + 1; }, CheckFlags(VMENU_SHOWAMPERSAND), TextAreaWidth, Policy); +//} + +// TBD: Remove +//bool VMenu::ShiftItemHPos(MenuItemEx& Item, const int Shift, const int TextAreaWidth, const item_hscroll_policy Policy) +//{ +// return SetItemAbsoluteHPos(Item, Item.HPos + Shift, TextAreaWidth, Policy); +//} +// +//bool VMenu::ShiftAllItemsHPos(const int Shift, const item_hscroll_policy Policy) +//{ +// const auto TextAreaWidth{ CalculateTextAreaWidth() }; +// +// return std::accumulate(Items.begin(), Items.end(), false, [&](const bool NeedRedraw, MenuItemEx& Item) +// { +// return ShiftItemHPos(Item, Shift, TextAreaWidth, Policy) +// || NeedRedraw; +// }); +//} void VMenu::Show() { @@ -3501,32 +3528,31 @@ TEST_CASE("item.hpos.limits") static const struct test_data { int ItemLength; - int MaxItemLength; int TextAreaWidth; // unbound, cling_to_edge, bound, bound_stick_to_left std::initializer_list> Expected; } TestDataPoints[] = { - { 1, 10, 5, { { -9, 13 }, { 0, 4 }, { 0, 4 }, { 0, 0 } } }, - { 3, 10, 5, { { -9, 11 }, { -2, 4 }, { 0, 2 }, { 0, 0 } } }, - { 5, 10, 5, { { -9, 9 }, { -4, 4 }, { 0, 0 }, { 0, 0 } } }, - { 7, 10, 5, { { -9, 7 }, { -6, 4 }, { -2, 0 }, { -2, 0 } } }, - { 10, 10, 5, { { -9, 4 }, { -9, 4 }, { -5, 0 }, { -5, 0 } } }, - { 1, 5, 5, { { -4, 8 }, { 0, 4 }, { 0, 4 }, { 0, 0 } } }, - { 3, 5, 5, { { -4, 6 }, { -2, 4 }, { 0, 2 }, { 0, 0 } } }, - { 5, 5, 5, { { -4, 4 }, { -4, 4 }, { 0, 0 }, { 0, 0 } } }, - { 1, 5, 10, { { -4, 13 }, { 0, 9 }, { 0, 9 }, { 0, 0 } } }, - { 3, 5, 10, { { -4, 11 }, { -2, 9 }, { 0, 7 }, { 0, 0 } } }, - { 5, 5, 10, { { -4, 9 }, { -4, 9 }, { 0, 5 }, { 0, 0 } } }, + { 1, 5, { { 0, 4 }, { 0, 4 }, { 0, 0 } } }, + { 3, 5, { { -2, 4 }, { 0, 2 }, { 0, 0 } } }, + { 5, 5, { { -4, 4 }, { 0, 0 }, { 0, 0 } } }, + { 7, 5, { { -6, 4 }, { -2, 0 }, { -2, 0 } } }, + { 10, 5, { { -9, 4 }, { -5, 0 }, { -5, 0 } } }, + { 1, 5, { { 0, 4 }, { 0, 4 }, { 0, 0 } } }, + { 3, 5, { { -2, 4 }, { 0, 2 }, { 0, 0 } } }, + { 5, 5, { { -4, 4 }, { 0, 0 }, { 0, 0 } } }, + { 1, 10, { { 0, 9 }, { 0, 9 }, { 0, 0 } } }, + { 3, 10, { { -2, 9 }, { 0, 7 }, { 0, 0 } } }, + { 5, 10, { { -4, 9 }, { 0, 5 }, { 0, 0 } } }, }; for (const auto& TestDataPoint : TestDataPoints) { for (const auto& Policy : - { item_hscroll_policy::unbound, item_hscroll_policy::cling_to_edge, item_hscroll_policy::bound, item_hscroll_policy::bound_stick_to_left }) + { item_hscroll_policy::cling_to_edge, item_hscroll_policy::bound, item_hscroll_policy::bound_stick_to_left }) { REQUIRE(std::ranges::data(TestDataPoint.Expected)[std::to_underlying(Policy)] - == item_hpos_limits(Policy, TestDataPoint.ItemLength, TestDataPoint.MaxItemLength, TestDataPoint.TextAreaWidth)); + == item_hpos_limits(Policy, TestDataPoint.ItemLength, TestDataPoint.TextAreaWidth)); } } } diff --git a/far/vmenu.hpp b/far/vmenu.hpp index 6a60d37625..8d9a46036f 100644 --- a/far/vmenu.hpp +++ b/far/vmenu.hpp @@ -309,13 +309,10 @@ class VMenu final: public Modal - [[nodiscard]] bool SetItemAbsoluteHPos(MenuItemEx& Item, int NewHPos, int ItemLength, int TextAreaWidth, item_hscroll_policy Policy); - [[nodiscard]] bool SetItemAbsoluteHPos(MenuItemEx& Item, int NewHPos, int TextAreaWidth, item_hscroll_policy Policy); + //[[nodiscard]] bool SetItemAbsoluteHPos(MenuItemEx& Item, int NewHPos, int ItemLength, int TextAreaWidth, item_hscroll_policy Policy); + //[[nodiscard]] bool SetItemAbsoluteHPos(MenuItemEx& Item, int NewHPos, int TextAreaWidth, item_hscroll_policy Policy); // Negative NewHPos is relative to the right edge, e.i., -1 aligns the item to the right - [[nodiscard]] bool SetItemSmartHPos(MenuItemEx& Item, int NewHPos, int TextAreaWidth, item_hscroll_policy Policy); - // Shifts item's HPos; if Shift is positive, the item visually moves right - [[nodiscard]] bool ShiftItemHPos(MenuItemEx& Item, int Shift, int TextAreaWidth, item_hscroll_policy Policy); - [[nodiscard]] bool ShiftAllItemsHPos(int Shift, item_hscroll_policy Policy); + //[[nodiscard]] bool SetItemSmartHPos(MenuItemEx& Item, int NewHPos, int TextAreaWidth, item_hscroll_policy Policy); void UpdateMaxLengthFromTitles(); void UpdateMaxLength(size_t Length);