Skip to content

Commit 6e6b70c

Browse files
committed
Fullwidth-aware VMenu rendering and other improvements.
1 parent 0b511cb commit 6e6b70c

9 files changed

Lines changed: 172 additions & 111 deletions

File tree

far/changelog

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,8 @@
1+
--------------------------------------------------------------------------------
2+
MZK 2026-01-04 17:57:18-05:00 - build 6626
3+
4+
1. Fullwidth-aware VMenu rendering and other improvements.
5+
16
--------------------------------------------------------------------------------
27
w17 2026-01-04 21:12:17+03:00 - build 6625
38

far/common.tests.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4444
// Platform:
4545

4646
// Common:
47+
#include "common/segment.hpp"
4748

4849
// External:
4950

far/common/algorithm.hpp

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3434

3535
#include "exception.hpp"
3636
#include "preprocessor.hpp"
37-
#include "segment.hpp"
3837
#include "type_traits.hpp"
3938

4039
#include <algorithm>
@@ -127,19 +126,4 @@ constexpr bool none_of(auto const&... Args)
127126
return !any_of(Args...);
128127
}
129128

130-
template<typename TA, typename TB, typename TC = std::common_type_t<TA, TB>>
131-
segment_t<TC> intersect(segment_t<TA> const A, segment_t<TB> const B)
132-
{
133-
if (A.empty() || B.empty())
134-
return {};
135-
136-
if (B.start() < A.start())
137-
return intersect(B, A);
138-
139-
if (A.end() <= B.start())
140-
return {};
141-
142-
return { static_cast<TC>(B.start()), typename segment_t<TC>::sentinel_tag{ std::min(static_cast<TC>(A.end()), static_cast<TC>(B.end())) } };
143-
}
144-
145129
#endif // ALGORITHM_HPP_BBD588C0_4752_46B2_AAB9_65450622FFF0

far/common/segment.hpp

Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,8 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
3636
#include <limits>
3737
#include <ranges>
3838

39+
#include "2d/rectangle.hpp"
40+
3941
//----------------------------------------------------------------------------
4042

4143
template<typename T>
@@ -92,6 +94,20 @@ class segment_t
9294
: segment_t{ InitialPoint, length_tag{ domain_max() } };
9395
}
9496

97+
template<typename U>
98+
[[nodiscard]]
99+
static constexpr segment_t horizontal_extent(const rectangle_t<U>& rect) noexcept
100+
{
101+
return { rect.left, length_tag{ rect.width() } };
102+
}
103+
104+
template<typename U>
105+
[[nodiscard]]
106+
static constexpr segment_t vertical_extent(const rectangle_t<U>& rect) noexcept
107+
{
108+
return { rect.top, length_tag{ rect.height() } };
109+
}
110+
95111
private:
96112
constexpr segment_t(T const Start, T const End) noexcept
97113
: m_Start{ Start }
@@ -105,6 +121,21 @@ class segment_t
105121
T m_End{}; // One past last
106122
};
107123

124+
template<typename TA, typename TB, typename TC = std::common_type_t<TA, TB>>
125+
segment_t<TC> intersect(segment_t<TA> const A, segment_t<TB> const B)
126+
{
127+
if (A.empty() || B.empty())
128+
return {};
129+
130+
if (B.start() < A.start())
131+
return intersect(B, A);
132+
133+
if (A.end() <= B.start())
134+
return {};
135+
136+
return { static_cast<TC>(B.start()), typename segment_t<TC>::sentinel_tag{ std::min(static_cast<TC>(A.end()), static_cast<TC>(B.end())) } };
137+
}
138+
108139
using small_segment = segment_t<short>;
109140
using segment = segment_t<int>;
110141

far/interf.cpp

Lines changed: 52 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -914,32 +914,71 @@ static void string_to_cells(string_view Str, size_t& CharsConsumed, cells& Cells
914914
void chars_to_cells(string_view Str, size_t& CharsConsumed, size_t const CellsAvailable, size_t& CellsConsumed)
915915
{
916916
cells Cells;
917-
const auto& CellsToBeConsumed = Cells.emplace<0>();
917+
const auto& CellsToBeConsumed = Cells.emplace<size_t>();
918918
string_to_cells(Str, CharsConsumed, Cells, CellsAvailable);
919919
CellsConsumed = CellsToBeConsumed;
920920

921-
#ifdef _DEBUG
922-
if (CharsConsumed == Str.size())
923-
assert(CellsConsumed == visual_string_length(Str));
924-
#endif
921+
assert(CharsConsumed < Str.size() || CellsConsumed == visual_string_length(Str));
925922
}
926923

927-
size_t Text(string_view Str, size_t const CellsAvailable)
924+
// Does not advance CurX
925+
static void write_text(string_view Str, size_t& CharsConsumed, size_t const CellsAvailable, size_t& CellsConsumed)
928926
{
929-
if (Str.empty())
930-
return 0;
927+
CharsConsumed = 0;
928+
CellsConsumed = 0;
931929

932-
cells Cells;
933-
const auto& Buffer = Cells.emplace<1>();
930+
if (Str.empty()) return;
934931

935-
size_t CharsConsumed = 0;
932+
cells Cells;
933+
const auto& Buffer = Cells.emplace<real_cells>();
936934

937935
string_to_cells(Str, CharsConsumed, Cells, CellsAvailable);
938936

939937
Global->ScrBuf->Write(CurX, CurY, Buffer);
940-
CurX += static_cast<int>(Buffer.size());
941938

942-
return Buffer.size();
939+
CellsConsumed = Buffer.size();
940+
}
941+
942+
// Returns true if all cells were consumed
943+
bool ClippedText(string_view Str, const segment Bounds, bool& AllCharsConsumed)
944+
{
945+
const auto WriteOrSkip{ [&Str](const auto RightBoundary, const auto Operation)
946+
{
947+
if (Str.empty() || CurX >= RightBoundary) return;
948+
949+
size_t CharsConsumed{};
950+
size_t CellsConsumed{};
951+
Operation(Str, CharsConsumed, RightBoundary - CurX, CellsConsumed);
952+
Str = Str.substr(CharsConsumed);
953+
CurX += static_cast<int>(CellsConsumed);
954+
} };
955+
956+
WriteOrSkip(Bounds.start(), chars_to_cells);
957+
WriteOrSkip(Bounds.end(), write_text);
958+
959+
AllCharsConsumed = Str.empty();
960+
if (AllCharsConsumed || CurX >= Bounds.end())
961+
return CurX >= Bounds.end();
962+
963+
assert(char_width::is_wide(encoding::utf16::extract_codepoint(Str)));
964+
assert(CurX == Bounds.end() - 1);
965+
return true;
966+
}
967+
968+
// Returns true if all cells were consumed
969+
bool ClippedText(string_view Str, const segment Bounds)
970+
{
971+
bool AllCharsConsumed{};
972+
return ClippedText(Str, Bounds, AllCharsConsumed);
973+
}
974+
975+
size_t Text(string_view Str, size_t const CellsAvailable)
976+
{
977+
size_t CharsConsumed{};
978+
size_t CellsConsumed{};
979+
write_text(Str, CharsConsumed, CellsAvailable, CellsConsumed);
980+
CurX += static_cast<int>(CellsConsumed);
981+
return CellsConsumed;
943982
}
944983

945984
size_t Text(string_view Str)

far/interf.hpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,6 +44,7 @@ THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
4444
#include "common/2d/matrix.hpp"
4545
#include "common/2d/rectangle.hpp"
4646
#include "common/function_ref.hpp"
47+
#include "common/segment.hpp"
4748
#include "common/singleton.hpp"
4849

4950
// External:
@@ -177,6 +178,10 @@ bool is_valid_surrogate_pair(wchar_t First, wchar_t Second);
177178

178179
void Text(point Where, const FarColor& Color, string_view Str);
179180

181+
// Returns true if all cells were consumed
182+
bool ClippedText(string_view Str, const segment Bounds, bool& AllCharsConsumed);
183+
bool ClippedText(string_view Str, const segment Bounds);
184+
180185
size_t Text(string_view Str, size_t CellsAvailable);
181186
size_t Text(string_view Str);
182187

far/vbuild.m4

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
6625
1+
6626

0 commit comments

Comments
 (0)