-
Notifications
You must be signed in to change notification settings - Fork 37
/
oof.h
1319 lines (1075 loc) · 44.8 KB
/
oof.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
#pragma once
#include <functional>
#include <optional>
#include <string>
#include <variant>
#include <vector>
#include <cstdint>
namespace oof
{
// Feel free to bit_cast, reinterpret_cast or memcpy your 3-byte color type into this.
struct color {
uint8_t red{}, green{}, blue{};
constexpr color() = default;
template<std::integral component_type>
constexpr color(component_type r, component_type g, component_type b)
: red{ static_cast<uint8_t>(r) }
, green{ static_cast<uint8_t>(g) }
, blue{ static_cast<uint8_t>(b) }
{}
template<std::integral component_type>
constexpr color(component_type component)
: color(component, component, component)
{}
friend constexpr auto operator==(const color&, const color&) -> bool = default;
};
// Necessary forward declarations
struct fg_rgb_color_sequence; struct fg_index_color_sequence;
struct bg_rgb_color_sequence; struct bg_index_color_sequence;
struct set_index_color_sequence;
struct bold_sequence; struct cursor_visibility_sequence; struct underline_sequence;
struct position_sequence; struct hposition_sequence; struct vposition_sequence;
struct store_position_sequence; struct load_position_sequence;
struct move_left_sequence; struct move_right_sequence; struct move_up_sequence; struct move_down_sequence;
struct char_sequence; struct wchar_sequence;
struct reset_sequence; struct clear_screen_sequence;
// Sets the foreground RGB color
[[nodiscard]] auto fg_color(const color& col) -> fg_rgb_color_sequence;
// Sets the foreground indexed color. Index must be in [1, 255]. You can define colors with set_index_color().
[[nodiscard]] auto fg_color(int index) -> fg_index_color_sequence;
// Sets the background RGB color
[[nodiscard]] auto bg_color(const color& col) -> bg_rgb_color_sequence;
// Sets the background indexed color. Index must be in [1, 255]. You can define colors with set_index_color().
[[nodiscard]] auto bg_color(int index) -> bg_index_color_sequence;
// Sets the indexed color. Index must be in [1, 255].
[[nodiscard]] auto set_index_color(int index, const color& col) -> set_index_color_sequence;
// Sets the underline state of the console
[[nodiscard]] auto underline(bool new_value = true) -> underline_sequence;
// Sets the bold state of the console. Warning: Bold is not supported by all console, see readme
[[nodiscard]] auto bold(bool new_value = true) -> bold_sequence;
// Sets cursor visibility state. Recommended to turn off before doing real-time displays
[[nodiscard]] auto cursor_visibility(bool new_value) -> cursor_visibility_sequence;
// Resets foreground- and background color, underline and bold state
[[nodiscard]] auto reset_formatting() -> reset_sequence;
// Clears the screen
[[nodiscard]] auto clear_screen() -> clear_screen_sequence;
// Sets the cursor position. Zero-based
[[nodiscard]] auto position(int line, int column) -> position_sequence;
[[nodiscard]] auto vposition(int line) -> vposition_sequence;
[[nodiscard]] auto hposition(int column) -> hposition_sequence;
[[nodiscard]] auto store_position() -> store_position_sequence;
[[nodiscard]] auto load_position() -> load_position_sequence;
// Moves the cursor a certain amount
[[nodiscard]] auto move_left(int amount) -> move_left_sequence;
[[nodiscard]] auto move_right(int amount) -> move_right_sequence;
[[nodiscard]] auto move_up(int amount) -> move_up_sequence;
[[nodiscard]] auto move_down(int amount) -> move_down_sequence;
using error_callback_type = void(*)(const std::string& msg);
inline error_callback_type error_callback = nullptr;
template<typename T, typename ... types>
constexpr bool is_any_of = (std::same_as<T, types> || ...);
template<typename T>
concept std_string_type = is_any_of<T, std::string, std::wstring>;
template<typename T, typename variant_type>
struct is_alternative : std::false_type {};
template<typename T, typename ... variant_alternatives>
struct is_alternative<T, std::variant<variant_alternatives...>>
: std::disjunction<std::is_same<T, variant_alternatives>...> {};
template<typename T, typename variant_type>
constexpr bool is_alternative_v = is_alternative<T, variant_type>::value;
using sequence_variant_type = std::variant<
fg_rgb_color_sequence, fg_index_color_sequence, bg_index_color_sequence, bg_rgb_color_sequence, set_index_color_sequence,
position_sequence, hposition_sequence, vposition_sequence, store_position_sequence, load_position_sequence,
underline_sequence, bold_sequence, char_sequence, wchar_sequence, reset_sequence, clear_screen_sequence, cursor_visibility_sequence,
move_left_sequence, move_right_sequence, move_up_sequence, move_down_sequence
>;
template<typename T>
concept sequence_c = is_alternative_v<T, sequence_variant_type>;
// Writes a single sequence type into a string
template<oof::std_string_type string_type, oof::sequence_c sequence_type>
auto write_sequence_into_string(string_type& target, const sequence_type& sequence) -> void;
// Returns a sing from a sequence type
template<oof::std_string_type string_type, oof::sequence_c sequence_type>
[[nodiscard]] auto get_string_from_sequence(const sequence_type& sequence) -> string_type;
// Returns a string from a vector of sequence types
template<oof::std_string_type string_type>
[[nodiscard]] auto get_string_from_sequences(const std::vector<sequence_variant_type>& sequences) -> string_type;
// Returns the exact size a string from this vector of sequence types
[[nodiscard]] auto get_string_reserve_size(const std::vector<sequence_variant_type>& sequences) -> size_t;
struct cell_format {
bool m_underline = false;
bool m_bold = false;
color m_fg_color{255, 255, 255};
color m_bg_color{0, 0, 0};
friend constexpr auto operator==(const cell_format&, const cell_format&) -> bool = default;
};
template<oof::std_string_type string_type>
struct cell {
using char_type = typename string_type::value_type;
char_type m_letter{};
cell_format m_format{};
friend constexpr auto operator==(const cell&, const cell&) -> bool = default;
};
template<oof::std_string_type string_type>
struct screen{
using char_type = typename string_type::value_type;
explicit screen(int width, int height, int start_column, int start_line, const cell<string_type>& background);
// This constructor taking a fill_char implies black background, white foreground color
explicit screen(int width, int height, int start_column, int start_line, char_type fill_char);
// This constructor taking a fill_char implies black background, white foreground color and top left start
explicit screen(int width, int height, char_type fill_char);
[[nodiscard]] auto get_width() const -> int;
[[nodiscard]] auto get_height() const -> int;
[[nodiscard]] auto get_cell (int column, int line) -> cell<string_type>&;
[[nodiscard]] auto is_inside(int column, int line) const -> bool;
[[nodiscard]] auto get_string( ) const -> string_type;
auto get_string(string_type& buffer) const -> void;
// This writes a text into the screen cells
auto write_into(const string_type& text, int column, int line, const cell_format& formatting) -> void;
// Override all cells with the background state
auto clear() -> void;
[[nodiscard]] auto begin() const { return std::begin(m_cells); }
[[nodiscard]] auto begin() { return std::begin(m_cells); }
[[nodiscard]] auto end() const { return std::end(m_cells); }
[[nodiscard]] auto end() { return std::end(m_cells); }
private:
auto update_sequence_buffer() const -> void;
int m_width = 0;
int m_height = 0;
int m_origin_line = 0;
int m_origin_column = 0;
cell<string_type> m_background;
std::vector<cell<string_type>> m_cells;
mutable std::vector<cell<string_type>> m_old_cells;
mutable std::vector<sequence_variant_type> m_sequence_buffer;
};
struct pixel_screen {
std::vector<color> m_pixels;
explicit pixel_screen(int width, int halfline_height, int start_column, int start_halfline, const color& fill_color);
// This will init with black fill color
explicit pixel_screen(int width, int halfline_height, int start_column, int start_halfline);
// This will init with black fill color and starting at the top left
explicit pixel_screen(int width, int halfline_height);
[[nodiscard]] auto begin() const { return std::begin(m_pixels); }
[[nodiscard]] auto begin() { return std::begin(m_pixels); }
[[nodiscard]] auto end() const { return std::end(m_pixels); }
[[nodiscard]] auto end() { return std::end(m_pixels); }
[[nodiscard]] auto get_string( ) const -> std::wstring;
auto get_string(std::wstring& buffer) const -> void;
[[nodiscard]] auto get_width() const -> int;
[[nodiscard]] auto get_halfline_height() const -> int;
// If you want to override something in the screen
[[nodiscard]] auto get_screen_ref() -> screen<std::wstring>&;
// Override all pixels with the fill color
auto clear() -> void;
[[nodiscard]] auto get_color(int column, int halfline) const -> const color&;
[[nodiscard]] auto get_color(int column, int halfline) -> color&;
[[nodiscard]] auto is_in (int column, int halfline) const -> bool;
private:
[[nodiscard]] auto get_line_height() const -> int;
auto compute_result() const -> void;
color m_fill_color{};
int m_halfline_height = 0; // This refers to "pixel" height. Height in lines will be half that.
int m_origin_column = 0;
int m_origin_halfline = 0;
mutable screen<std::wstring> m_screen;
};
// Deduction guide
template<typename char_type>
screen(int, int, int, int, char_type fill_char) -> screen<std::basic_string<char_type>>;
template<typename char_type>
screen(int, int, char_type fill_char) -> screen<std::basic_string<char_type>>;
template<typename stream_type, oof::sequence_c sequence_type>
auto operator<<(stream_type& os, const sequence_type& sequence) -> stream_type&;
namespace detail
{
// CRTP to extend the numerous sequence types with convenience member functions without using runtime
// polymorphism or repeating the code
template<typename T>
struct extender {
operator std::string() const;
operator std::wstring() const;
[[nodiscard]] auto operator+(const std::string& other) const -> std::string;
[[nodiscard]] auto operator+(const std::wstring& other) const -> std::wstring;
};
auto error(const std::string& msg) -> void;
[[nodiscard]] auto get_pixel_background(const color& fill_color) -> cell<std::wstring>;
template<oof::std_string_type string_type>
auto write_sequence_string_no_reserve(const std::vector<sequence_variant_type>& sequences, string_type& target) -> void;
template<oof::sequence_c sequence_type>
[[nodiscard]] constexpr auto get_sequence_string_size(const sequence_type& sequence) -> size_t;
template<oof::std_string_type string_type, std::integral int_type>
auto write_int_to_string(string_type& target, const int_type value, const bool with_leading_semicolon) -> void;
struct cell_pos {
int m_index = 0;
int m_width = 0;
int m_height = 0;
explicit constexpr cell_pos(const int width, const int height)
: m_width(width)
, m_height(height)
{}
[[nodiscard]] constexpr auto get_column() const -> int {
return m_index % m_width;
}
[[nodiscard]] constexpr auto get_line() const -> int {
return m_index / m_width;
}
[[nodiscard]] constexpr auto is_end() const -> bool {
return m_index >= (m_width * m_height);
}
[[nodiscard]] constexpr auto operator+(const int jump_amount) const -> cell_pos {
cell_pos jumped_pos(m_width, m_height);
jumped_pos.m_index = m_index + jump_amount;
return jumped_pos;
}
constexpr auto operator++() -> cell_pos& {
++m_index;
return *this;
}
friend constexpr auto operator==(const cell_pos&, const cell_pos&) -> bool = default;
};
template<oof::std_string_type string_type>
struct draw_state{
using cell_type = cell<string_type>;
std::optional<cell_pos> m_last_written_pos;
std::optional<cell_format> m_format;
explicit draw_state() = default;
auto write_sequence(
std::vector<sequence_variant_type>& sequence_buffer,
const cell_type& target_cell_state,
const std::optional<std::reference_wrapper<const cell_type>>& old_cell_state,
const cell_pos& target_pos,
const int origin_line,
const int origin_column
) -> void;
private:
[[nodiscard]] auto is_position_sequence_necessary(const cell_pos& target_pos) const -> bool;
};
template<oof::std_string_type string_type, typename T, typename ... Ts>
auto write_ints_into_string(string_type& target, const T& first, const Ts&... rest) -> void;
template<oof::std_string_type string_type>
[[nodiscard]] auto get_index_color_seq_str(const set_index_color_sequence& sequence) -> string_type;
template<std_string_type string_type>
using fitting_char_sequence_t = std::conditional_t<std::is_same_v<string_type, std::string>, char_sequence, wchar_sequence>;
} // namespace detail
struct fg_rgb_color_sequence : detail::extender<fg_rgb_color_sequence> {
color m_color;
};
struct fg_index_color_sequence : detail::extender<fg_index_color_sequence> {
int m_index;
};
struct set_index_color_sequence : detail::extender<set_index_color_sequence> {
int m_index{};
color m_color;
};
struct bg_rgb_color_sequence : detail::extender<bg_rgb_color_sequence> {
color m_color;
};
struct bg_index_color_sequence : detail::extender<bg_index_color_sequence> {
int m_index;
};
struct underline_sequence : detail::extender<underline_sequence> {
bool m_underline;
};
struct bold_sequence : detail::extender<bold_sequence> {
bool m_bold;
};
struct cursor_visibility_sequence : detail::extender<cursor_visibility_sequence> {
bool m_visibility;
};
struct position_sequence : detail::extender<position_sequence> {
uint8_t m_line;
uint8_t m_column;
};
struct hposition_sequence : detail::extender<hposition_sequence> {
uint8_t m_column;
};
struct vposition_sequence : detail::extender<vposition_sequence> {
uint8_t m_line;
};
struct store_position_sequence : detail::extender<store_position_sequence> {};
struct load_position_sequence : detail::extender<load_position_sequence> {};
struct move_left_sequence : detail::extender<move_left_sequence> {
uint8_t m_amount;
};
struct move_right_sequence : detail::extender<move_right_sequence> {
uint8_t m_amount;
};
struct move_up_sequence : detail::extender<move_up_sequence> {
uint8_t m_amount;
};
struct move_down_sequence : detail::extender<move_down_sequence> {
uint8_t m_amount;
};
struct char_sequence : detail::extender<char_sequence> {
char m_letter;
};
struct wchar_sequence : detail::extender<wchar_sequence> {
wchar_t m_letter;
};
struct reset_sequence : detail::extender<reset_sequence> {};
struct clear_screen_sequence : detail::extender<clear_screen_sequence> {};
} // namespace oof
// Constexpr, therefore defined here
template<oof::sequence_c sequence_type>
constexpr auto oof::detail::get_sequence_string_size(const sequence_type& sequence) -> size_t
{
constexpr auto get_int_param_str_length = [](const int param) -> int {
if (param < 10) return 1;
if (param < 100) return 2;
return 3;
};
if constexpr (is_any_of<sequence_type, char_sequence, wchar_sequence>) {
return 1;
}
else if constexpr (std::is_same_v<sequence_type, set_index_color_sequence>) {
size_t reserve_size = 0;
reserve_size += 4; // \x1b]4;
reserve_size += get_int_param_str_length(sequence.m_index); // <i>;
reserve_size += 4; // ;rgb:
constexpr auto get_component_str_size = [](const uint8_t component) {
return component < 15 ? 2 : 1;
};
reserve_size += get_component_str_size(sequence.m_color.red);
reserve_size += 1; // /
reserve_size += get_component_str_size(sequence.m_color.green);
reserve_size += 1; // /
reserve_size += get_component_str_size(sequence.m_color.blue);
reserve_size += 2; // <ST>
return reserve_size;
}
else {
size_t reserve_size = 0;
constexpr int semicolon_size = 1;
if constexpr (is_any_of<sequence_type, fg_rgb_color_sequence, bg_rgb_color_sequence>) {
reserve_size += 2 + semicolon_size + 1 +
semicolon_size + get_int_param_str_length(sequence.m_color.red) +
semicolon_size + get_int_param_str_length(sequence.m_color.green) +
semicolon_size + get_int_param_str_length(sequence.m_color.blue);
}
else if constexpr (std::is_same_v<sequence_type, underline_sequence>)
{
reserve_size += sequence.m_underline ? 1 : 2;
}
else if constexpr (std::is_same_v<sequence_type, bold_sequence>)
{
reserve_size += sequence.m_bold ? 1 : 2;
}
else if constexpr (std::is_same_v<sequence_type, position_sequence>)
{
reserve_size += get_int_param_str_length(sequence.m_line);
reserve_size += semicolon_size;
reserve_size += get_int_param_str_length(sequence.m_column);
}
else if constexpr (std::is_same_v<sequence_type, hposition_sequence>) {
reserve_size += get_int_param_str_length(sequence.m_column);
}
else if constexpr (std::is_same_v<sequence_type, vposition_sequence>) {
reserve_size += get_int_param_str_length(sequence.m_line);
}
else if constexpr (is_any_of<sequence_type, reset_sequence, clear_screen_sequence>)
{
reserve_size += 1;
}
else if constexpr (is_any_of<sequence_type, cursor_visibility_sequence>)
{
reserve_size += 3;
}
else if constexpr (is_any_of<sequence_type, move_left_sequence, move_right_sequence, move_up_sequence, move_down_sequence>)
{
reserve_size += get_int_param_str_length(sequence.m_amount);
}
else if constexpr (std::is_same_v<sequence_type, fg_index_color_sequence>)
{
reserve_size += 5; // "38;5;"
reserve_size += get_int_param_str_length(sequence.m_index);
}
reserve_size += 3; // 2 intro, 1 outro
return reserve_size;
}
}
// This will deliberately be instantiated at compiletime
template<typename stream_type, oof::sequence_c sequence_type>
auto oof::operator<<(stream_type& os, const sequence_type& sequence) -> stream_type&
{
using char_type = typename stream_type::char_type;
using string_type = std::basic_string<char_type>;
string_type temp_string{};
temp_string.reserve(detail::get_sequence_string_size(sequence));
write_sequence_into_string(temp_string, sequence);
os << temp_string;
return os;
}
#ifdef OOF_IMPL
// Instantiated by write_ints_into_string()
template<oof::std_string_type string_type, std::integral int_type>
auto oof::detail::write_int_to_string(
string_type& target,
const int_type value,
const bool with_leading_semicolon
) -> void
{
using char_type = typename string_type::value_type;
if (with_leading_semicolon)
target += static_cast<char_type>(';');
const int hundreds = value / 100;
if (value >= 100)
target += static_cast<char_type>('0' + hundreds);
if (value >= 10)
target += static_cast<char_type>('0' + (value % 100) / 10);
target += '0' + value % 10;
}
// Instantiated by write_sequence_into_string()
template<oof::std_string_type string_type, typename T, typename ... Ts>
auto oof::detail::write_ints_into_string(string_type& target, const T& first, const Ts&... rest) -> void
{
detail::write_int_to_string(target, first, false);
(detail::write_int_to_string(target, rest, true), ...);
}
// Instantiated by write_sequence_string_no_reserve()
template<oof::std_string_type string_type, oof::sequence_c sequence_type>
auto oof::write_sequence_into_string(
string_type& target,
const sequence_type& sequence
) -> void
{
if constexpr (std::is_same_v<sequence_type, detail::fitting_char_sequence_t<string_type>>)
{
target += sequence.m_letter;
}
else
{
using char_type = typename string_type::value_type;
target += static_cast<char_type>('\x1b');
if constexpr (std::same_as<sequence_type, set_index_color_sequence>)
target += static_cast<char_type>(']');
else if constexpr (std::same_as<sequence_type, store_position_sequence> || std::same_as<sequence_type, load_position_sequence>)
{
}
else
target += static_cast<char_type>('[');
if constexpr (std::is_same_v<sequence_type, fg_rgb_color_sequence>)
{
detail::write_ints_into_string(target, 38, 2, sequence.m_color.red, sequence.m_color.green, sequence.m_color.blue);
target += static_cast<char_type>('m');
}
else if constexpr (std::is_same_v<sequence_type, fg_index_color_sequence>)
{
detail::write_ints_into_string(target, 38, 5, sequence.m_index);
target += static_cast<char_type>('m');
}
else if constexpr (std::is_same_v<sequence_type, bg_index_color_sequence>)
{
detail::write_ints_into_string(target, 48, 5, sequence.m_index);
target += static_cast<char_type>('m');
}
else if constexpr (std::is_same_v<sequence_type, set_index_color_sequence>)
{
detail::write_ints_into_string(target, 4, sequence.m_index);
target += detail::get_index_color_seq_str<string_type>(sequence);
}
else if constexpr (std::is_same_v<sequence_type, bg_rgb_color_sequence>)
{
detail::write_ints_into_string(target, 48, 2, sequence.m_color.red, sequence.m_color.green, sequence.m_color.blue);
target += static_cast<char_type>('m');
}
else if constexpr (std::is_same_v<sequence_type, underline_sequence>)
{
detail::write_ints_into_string(target, sequence.m_underline ? 4 : 24);
target += static_cast<char_type>('m');
}
else if constexpr (std::is_same_v<sequence_type, bold_sequence>)
{
detail::write_ints_into_string(target, sequence.m_bold ? 1 : 22);
target += static_cast<char_type>('m');
}
else if constexpr (std::is_same_v<sequence_type, cursor_visibility_sequence>)
{
target += static_cast<char_type>('?');
detail::write_ints_into_string(target, 25);
target += static_cast<char_type>(sequence.m_visibility ? 'h' : 'l');
}
else if constexpr (std::is_same_v<sequence_type, position_sequence>)
{
detail::write_ints_into_string(target, sequence.m_line + 1, sequence.m_column + 1);
target += static_cast<char_type>('H');
}
else if constexpr (std::is_same_v<sequence_type, hposition_sequence>)
{
detail::write_ints_into_string(target, sequence.m_column + 1);
target += static_cast<char_type>('G');
}
else if constexpr (std::is_same_v<sequence_type, vposition_sequence>)
{
detail::write_ints_into_string(target, sequence.m_line + 1);
target += static_cast<char_type>('d');
}
else if constexpr (std::is_same_v<sequence_type, store_position_sequence>)
{
target += static_cast<char_type>('7');
}
else if constexpr (std::is_same_v<sequence_type, load_position_sequence>)
{
target += static_cast<char_type>('8');
}
else if constexpr (std::is_same_v<sequence_type, move_down_sequence>)
{
detail::write_ints_into_string(target, sequence.m_amount);
target += static_cast<char_type>('B');
}
else if constexpr (std::is_same_v<sequence_type, move_up_sequence>)
{
detail::write_ints_into_string(target, sequence.m_amount);
target += static_cast<char_type>('A');
}
else if constexpr (std::is_same_v<sequence_type, move_left_sequence>)
{
detail::write_ints_into_string(target, sequence.m_amount);
target += static_cast<char_type>('D');
}
else if constexpr (std::is_same_v<sequence_type, move_right_sequence>)
{
detail::write_ints_into_string(target, sequence.m_amount);
target += static_cast<char_type>('C');
}
else if constexpr (std::is_same_v<sequence_type, reset_sequence>)
{
detail::write_ints_into_string(target, 0);
target += static_cast<char_type>('m');
}
else if constexpr (std::is_same_v<sequence_type, clear_screen_sequence>)
{
detail::write_ints_into_string(target, 2);
target += static_cast<char_type>('J');
}
}
}
template<oof::std_string_type string_type>
auto oof::detail::get_index_color_seq_str(
const set_index_color_sequence& sequence
) -> string_type
{
using char_type = typename string_type::value_type;
string_type result;
if constexpr (std::same_as<string_type, std::string>)
result = ";rgb:";
else
result = L";rgb:";
const auto write_nibble = [&](const int nibble) {
if (nibble < 10)
result += static_cast<char_type>('0' + nibble);
else
result += static_cast<char_type>('a' + nibble - 10);
};
const auto write_component = [&](const uint8_t component) {
if (component > 15)
write_nibble(component >> 4);
write_nibble(component & 0xf);
};
write_component(sequence.m_color.red);
result += static_cast<char_type>('/');
write_component(sequence.m_color.green);
result += static_cast<char_type>('/');
write_component(sequence.m_color.blue);
result += static_cast<char_type>('\x1b');
result += static_cast<char_type>('\x5c');
return result;
}
template<oof::std_string_type string_type>
auto oof::screen<string_type>::update_sequence_buffer() const -> void
{
detail::draw_state<string_type> state{};
m_sequence_buffer.clear();
m_sequence_buffer.push_back(reset_sequence{});
for (detail::cell_pos relative_pos{ this->m_width, this->m_height }; relative_pos.is_end() == false; ++relative_pos)
{
const cell<string_type>& target_cell_state = this->m_cells[relative_pos.m_index];
std::optional<std::reference_wrapper<const cell<string_type>>> old_cell_state;
if (this->m_old_cells.empty() == false)
old_cell_state.emplace(this->m_old_cells[relative_pos.m_index]);
state.write_sequence(
m_sequence_buffer,
target_cell_state, old_cell_state,
relative_pos,
this->m_origin_line, this->m_origin_column
);
}
}
template<oof::std_string_type string_type>
oof::screen<string_type>::screen(
const int width, const int height,
const int start_column, const int start_line,
const cell<string_type>& background
)
: m_width(width)
, m_height(height)
, m_origin_line(start_line)
, m_origin_column(start_column)
, m_background(background)
, m_cells(width* height, background)
{
if (width <= 0)
{
const std::string msg = "Width can't be negative";
::oof::detail::error(msg);
}
if (height <= 0)
{
const std::string msg = "Height can't be negative";
::oof::detail::error(msg);
}
}
template<oof::std_string_type string_type>
oof::screen<string_type>::screen(
const int width, const int height,
const int start_column, const int start_line,
const char_type fill_char
)
: screen(width, height, start_column, start_line, cell<string_type>{fill_char})
{
}
template<oof::std_string_type string_type>
oof::screen<string_type>::screen(
const int width, const int height,
const char_type fill_char
)
: screen(width, height, 0, 0, fill_char)
{
}
template <oof::std_string_type string_type>
auto oof::screen<string_type>::get_width() const -> int
{
return m_width;
}
template <oof::std_string_type string_type>
auto oof::screen<string_type>::get_height() const -> int
{
return m_height;
}
template<oof::std_string_type string_type>
auto oof::screen<string_type>::get_string() const -> string_type
{
this->update_sequence_buffer();
string_type result = ::oof::get_string_from_sequences<string_type>(m_sequence_buffer);
m_old_cells = m_cells;
return result;
}
template<oof::std_string_type string_type>
auto oof::screen<string_type>::get_string(string_type& buffer) const -> void
{
this->update_sequence_buffer();
// Reserve if the string buffer is still empty (on the first call)
if (buffer.empty())
buffer.reserve(::oof::get_string_reserve_size(m_sequence_buffer));
buffer.clear();
::oof::detail::write_sequence_string_no_reserve(m_sequence_buffer, buffer);
m_old_cells = m_cells;
}
template <oof::std_string_type string_type>
auto oof::screen<string_type>::write_into(
const string_type& text,
const int column, const int line,
const cell_format& formatting
) -> void
{
if (line < 0 || line >= m_height)
{
std::string msg = "Line is out of range. Height is ";
msg += std::to_string(m_height);
msg += ", line was: ";
msg += std::to_string(line);
::oof::detail::error(msg);
return;
}
if (column < 0 || column >= m_width)
{
std::string msg = "Column is out of range. Width is ";
msg += std::to_string(m_width);
msg += ", column was: ";
msg += std::to_string(column);
::oof::detail::error(msg);
return;
}
const int ending_column = column + static_cast<int>(text.size());
if (ending_column > m_width)
{
::oof::detail::error("Trying to write_into() with a text that won't fit.");
return;
}
for (size_t i = 0; i < text.size(); ++i) {
cell<string_type>& cell = m_cells[line * m_width + column + i];
cell.m_letter = text[i];
cell.m_format = formatting;
}
}
template<oof::std_string_type string_type>
auto oof::screen<string_type>::is_inside(const int column, const int line) const -> bool
{
return column >= 0 && column < m_width&& line >= 0 && line < m_height;
}
template<oof::std_string_type string_type>
auto oof::screen<string_type>::get_cell(const int column, const int line) -> cell<string_type>&
{
if ( line < 0 || line >= m_height)
{
std::string msg = "Line is out of range. Height is ";
msg += std::to_string(m_height);
msg += ", line was: ";
msg += std::to_string(line);
::oof::detail::error(msg);
return m_cells[0];
}
if (column < 0 || column >= m_width)
{
std::string msg = "Column is out of range. Width is ";
msg += std::to_string(m_width);
msg += ", column was: ";
msg += std::to_string(column);
::oof::detail::error(msg);
return m_cells[0];
}
const int index = line * m_width + column;
return m_cells[index];
}
template struct oof::screen<std::string>;
template struct oof::screen<std::wstring>;
auto oof::get_string_reserve_size(const std::vector<sequence_variant_type>& sequences) -> size_t
{
size_t reserve_size{};
for (const sequence_variant_type& sequence : sequences)
std::visit([&](const auto& alternative) { reserve_size += detail::get_sequence_string_size(alternative); }, sequence);
return reserve_size;
}
// Instantiated by oof::screen<string_type>::get_string()
template<oof::std_string_type string_type>
auto oof::detail::write_sequence_string_no_reserve(
const std::vector<sequence_variant_type>& sequences,
string_type& target
) -> void
{
for (const sequence_variant_type& sequence : sequences)
std::visit([&](const auto& alternative) { write_sequence_into_string(target, alternative); }, sequence);
}
template<oof::std_string_type string_type>
auto oof::get_string_from_sequences(
const std::vector<sequence_variant_type>& sequences
) -> string_type
{
string_type result_str{};
result_str.reserve(::oof::get_string_reserve_size(sequences));
::oof::detail::write_sequence_string_no_reserve(sequences, result_str);
return result_str;
}
template auto oof::get_string_from_sequences(const std::vector<sequence_variant_type>& sequences) -> std::string;
template auto oof::get_string_from_sequences(const std::vector<sequence_variant_type>& sequences) -> std::wstring;
auto oof::position(const int line, const int column) -> position_sequence {
return position_sequence{
.m_line = static_cast<uint8_t>(line),
.m_column = static_cast<uint8_t>(column)
};
}
auto oof::vposition(const int line) -> vposition_sequence {
return vposition_sequence{ .m_line = static_cast<uint8_t>(line) };
}
auto oof::hposition(const int column) -> hposition_sequence {
return hposition_sequence{ .m_column = static_cast<uint8_t>(column) };
}
auto oof::store_position() -> store_position_sequence
{
return store_position_sequence{};
}
auto oof::load_position() -> load_position_sequence
{
return load_position_sequence{};
}
auto oof::move_left(const int amount) -> move_left_sequence
{
return move_left_sequence{ .m_amount = static_cast<uint8_t>(amount)};
}
auto oof::move_right(const int amount) -> move_right_sequence
{
return move_right_sequence{ .m_amount = static_cast<uint8_t>(amount) };
}
auto oof::move_up(const int amount) -> move_up_sequence
{
return move_up_sequence{ .m_amount = static_cast<uint8_t>(amount) };
}
auto oof::move_down(const int amount) -> move_down_sequence
{
return move_down_sequence{ .m_amount = static_cast<uint8_t>(amount) };
}
auto oof::fg_color(const color& col) -> fg_rgb_color_sequence {
return fg_rgb_color_sequence{ .m_color = col };
}
auto oof::fg_color(const int index) -> fg_index_color_sequence
{
if (index < 1 || index > 255)
{
std::string msg = "Index must be in [1, 255], was: ";
msg += std::to_string(index);
::oof::detail::error(msg);
return fg_index_color_sequence{ .m_index=1 };
}
return fg_index_color_sequence{ .m_index = index };
}
auto oof::set_index_color(
const int index,
const color& col
) -> set_index_color_sequence
{
if (index < 1 || index > 255)
{
std::string msg = "Index must be in [1, 255], was: ";
msg += std::to_string(index);
::oof::detail::error(msg);
return set_index_color_sequence{ .m_index=1, .m_color=col };
}
return set_index_color_sequence{ .m_index=index, .m_color=col };
}
auto oof::bg_color(const color& col) -> bg_rgb_color_sequence
{