mirror of
https://github.com/RGBCube/serenity
synced 2025-07-25 15:17:36 +00:00
LibLine: Add live styling support
This patchset adds an stylization interface to LibLine, and breaks multiline editing. With the most adorable Style constructor I've ever seen :^)
This commit is contained in:
parent
2c5faa8ff3
commit
c2f8a5fffa
4 changed files with 328 additions and 31 deletions
|
@ -35,6 +35,7 @@ namespace Line {
|
||||||
|
|
||||||
Editor::Editor()
|
Editor::Editor()
|
||||||
{
|
{
|
||||||
|
m_pending_chars = ByteBuffer::create_uninitialized(0);
|
||||||
struct winsize ws;
|
struct winsize ws;
|
||||||
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0)
|
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) < 0)
|
||||||
m_num_columns = 80;
|
m_num_columns = 80;
|
||||||
|
@ -67,22 +68,15 @@ void Editor::clear_line()
|
||||||
|
|
||||||
void Editor::insert(const String& string)
|
void Editor::insert(const String& string)
|
||||||
{
|
{
|
||||||
fputs(string.characters(), stdout);
|
m_pending_chars.append(string.characters(), string.length());
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
if (m_cursor == m_buffer.size()) {
|
if (m_cursor == m_buffer.size()) {
|
||||||
m_buffer.append(string.characters(), string.length());
|
m_buffer.append(string.characters(), string.length());
|
||||||
m_cursor = m_buffer.size();
|
m_cursor = m_buffer.size();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vt_save_cursor();
|
|
||||||
vt_clear_to_end_of_line();
|
|
||||||
for (size_t i = m_cursor; i < m_buffer.size(); ++i)
|
|
||||||
fputc(m_buffer[i], stdout);
|
|
||||||
vt_restore_cursor();
|
|
||||||
|
|
||||||
m_buffer.ensure_capacity(m_buffer.size() + string.length());
|
m_buffer.ensure_capacity(m_buffer.size() + string.length());
|
||||||
|
m_chars_inserted_in_the_middle += string.length();
|
||||||
for (size_t i = 0; i < string.length(); ++i)
|
for (size_t i = 0; i < string.length(); ++i)
|
||||||
m_buffer.insert(m_cursor + i, string[i]);
|
m_buffer.insert(m_cursor + i, string[i]);
|
||||||
m_cursor += string.length();
|
m_cursor += string.length();
|
||||||
|
@ -90,22 +84,15 @@ void Editor::insert(const String& string)
|
||||||
|
|
||||||
void Editor::insert(const char ch)
|
void Editor::insert(const char ch)
|
||||||
{
|
{
|
||||||
putchar(ch);
|
m_pending_chars.append(&ch, 1);
|
||||||
fflush(stdout);
|
|
||||||
|
|
||||||
if (m_cursor == m_buffer.size()) {
|
if (m_cursor == m_buffer.size()) {
|
||||||
m_buffer.append(ch);
|
m_buffer.append(ch);
|
||||||
m_cursor = m_buffer.size();
|
m_cursor = m_buffer.size();
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
vt_save_cursor();
|
|
||||||
vt_clear_to_end_of_line();
|
|
||||||
for (size_t i = m_cursor; i < m_buffer.size(); ++i)
|
|
||||||
fputc(m_buffer[i], stdout);
|
|
||||||
vt_restore_cursor();
|
|
||||||
|
|
||||||
m_buffer.insert(m_cursor, ch);
|
m_buffer.insert(m_cursor, ch);
|
||||||
|
++m_chars_inserted_in_the_middle;
|
||||||
++m_cursor;
|
++m_cursor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -118,6 +105,26 @@ void Editor::register_character_input_callback(char ch, Function<bool(Editor&)>
|
||||||
m_key_callbacks.set(ch, make<KeyCallback>(move(callback)));
|
m_key_callbacks.set(ch, make<KeyCallback>(move(callback)));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Editor::stylize(const Span& span, const Style& style)
|
||||||
|
{
|
||||||
|
auto starting_map = m_spans_starting.get(span.beginning()).value_or({});
|
||||||
|
|
||||||
|
if (!starting_map.contains(span.end()))
|
||||||
|
m_refresh_needed = true;
|
||||||
|
|
||||||
|
starting_map.set(span.end(), style);
|
||||||
|
|
||||||
|
m_spans_starting.set(span.beginning(), starting_map);
|
||||||
|
|
||||||
|
auto ending_map = m_spans_ending.get(span.end()).value_or({});
|
||||||
|
|
||||||
|
if (!ending_map.contains(span.beginning()))
|
||||||
|
m_refresh_needed = true;
|
||||||
|
ending_map.set(span.beginning(), style);
|
||||||
|
|
||||||
|
m_spans_ending.set(span.end(), ending_map);
|
||||||
|
}
|
||||||
|
|
||||||
void Editor::cut_mismatching_chars(String& completion, const String& other, size_t start_compare)
|
void Editor::cut_mismatching_chars(String& completion, const String& other, size_t start_compare)
|
||||||
{
|
{
|
||||||
size_t i = start_compare;
|
size_t i = start_compare;
|
||||||
|
@ -174,15 +181,7 @@ String Editor::get_line(const String& prompt)
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
m_buffer.remove(m_cursor);
|
m_buffer.remove(m_cursor);
|
||||||
fputs("\033[3~", stdout);
|
|
||||||
fflush(stdout);
|
|
||||||
vt_save_cursor();
|
|
||||||
vt_clear_to_end_of_line();
|
|
||||||
for (size_t i = m_cursor; i < m_buffer.size(); ++i)
|
|
||||||
fputc(m_buffer[i], stdout);
|
|
||||||
vt_restore_cursor();
|
|
||||||
};
|
};
|
||||||
|
|
||||||
for (ssize_t i = 0; i < nread; ++i) {
|
for (ssize_t i = 0; i < nread; ++i) {
|
||||||
char ch = keybuf[i];
|
char ch = keybuf[i];
|
||||||
if (ch == 0)
|
if (ch == 0)
|
||||||
|
@ -379,7 +378,7 @@ String Editor::get_line(const String& prompt)
|
||||||
do_backspace();
|
do_backspace();
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (ch == 0xc) { // ^L
|
if (ch == 0xc) { // ^L
|
||||||
printf("\033[3J\033[H\033[2J"); // Clear screen.
|
printf("\033[3J\033[H\033[2J"); // Clear screen.
|
||||||
fputs(prompt.characters(), stdout);
|
fputs(prompt.characters(), stdout);
|
||||||
for (size_t i = 0; i < m_buffer.size(); ++i)
|
for (size_t i = 0; i < m_buffer.size(); ++i)
|
||||||
|
@ -422,9 +421,111 @@ String Editor::get_line(const String& prompt)
|
||||||
|
|
||||||
insert(ch);
|
insert(ch);
|
||||||
}
|
}
|
||||||
|
refresh_display();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Editor::refresh_display()
|
||||||
|
{
|
||||||
|
if (on_display_refresh)
|
||||||
|
on_display_refresh(*this);
|
||||||
|
|
||||||
|
if (!m_refresh_needed && m_cursor == m_buffer.size()) {
|
||||||
|
// just write the characters out and continue
|
||||||
|
// no need to refresh the entire line
|
||||||
|
char null = 0;
|
||||||
|
m_pending_chars.append(&null, 1);
|
||||||
|
fputs((char*)m_pending_chars.data(), stdout);
|
||||||
|
m_pending_chars.clear();
|
||||||
|
fflush(stdout);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// ouch, reflow entire line
|
||||||
|
// FIXME: handle multiline stuff
|
||||||
|
vt_move_relative(0, m_pending_chars.size() - m_chars_inserted_in_the_middle);
|
||||||
|
vt_save_cursor();
|
||||||
|
auto current_line = cursor_line();
|
||||||
|
vt_clear_lines(current_line - 1, num_lines() - current_line);
|
||||||
|
vt_move_relative(-num_lines() + 1, -offset_in_line() + m_chars_inserted_in_the_middle);
|
||||||
|
vt_clear_to_end_of_line();
|
||||||
|
HashMap<u32, Style> empty_styles {};
|
||||||
|
for (size_t i = 0; i < m_buffer.size(); ++i) {
|
||||||
|
auto ends = m_spans_ending.get(i).value_or(empty_styles);
|
||||||
|
auto starts = m_spans_starting.get(i).value_or(empty_styles);
|
||||||
|
if (ends.size()) {
|
||||||
|
// go back to defaults
|
||||||
|
vt_apply_style(find_applicable_style(i));
|
||||||
|
}
|
||||||
|
if (starts.size()) {
|
||||||
|
// set new options
|
||||||
|
vt_apply_style(starts.begin()->value); // apply some random style that starts here
|
||||||
|
}
|
||||||
|
fputc(m_buffer[i], stdout);
|
||||||
|
}
|
||||||
|
vt_apply_style({}); // don't bleed to EOL
|
||||||
|
vt_restore_cursor();
|
||||||
|
vt_move_relative(0, m_chars_inserted_in_the_middle);
|
||||||
|
fflush(stdout);
|
||||||
|
m_pending_chars.clear();
|
||||||
|
m_refresh_needed = false;
|
||||||
|
m_chars_inserted_in_the_middle = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::vt_move_relative(int x, int y)
|
||||||
|
{
|
||||||
|
char x_op = 'A', y_op = 'D';
|
||||||
|
if (x > 0)
|
||||||
|
x_op = 'B';
|
||||||
|
else
|
||||||
|
x = -x;
|
||||||
|
if (y > 0)
|
||||||
|
y_op = 'C';
|
||||||
|
else
|
||||||
|
y = -y;
|
||||||
|
|
||||||
|
if (x > 0)
|
||||||
|
printf("\033[%d%c", x, x_op);
|
||||||
|
if (y > 0)
|
||||||
|
printf("\033[%d%c", y, y_op);
|
||||||
|
}
|
||||||
|
|
||||||
|
Style Editor::find_applicable_style(size_t offset) const
|
||||||
|
{
|
||||||
|
// walk through our styles and find one that fits in the offset
|
||||||
|
for (auto& entry : m_spans_starting) {
|
||||||
|
if (entry.key > offset)
|
||||||
|
continue;
|
||||||
|
for (auto& style_value : entry.value) {
|
||||||
|
if (style_value.key <= offset)
|
||||||
|
continue;
|
||||||
|
return style_value.value;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::vt_apply_style(const Style& style)
|
||||||
|
{
|
||||||
|
printf(
|
||||||
|
"\033[%d;%d;%d;%d;%dm",
|
||||||
|
style.bold() ? 1 : 22,
|
||||||
|
style.underline() ? 4 : 24,
|
||||||
|
style.italic() ? 3 : 23,
|
||||||
|
(int)style.foreground() + 30,
|
||||||
|
(int)style.background() + 40);
|
||||||
|
}
|
||||||
|
|
||||||
|
void Editor::vt_clear_lines(size_t count_above, size_t count_below)
|
||||||
|
{
|
||||||
|
// go down count_below lines
|
||||||
|
if (count_below > 0)
|
||||||
|
printf("\033[%dB", (int)count_below);
|
||||||
|
// then clear lines going upwards
|
||||||
|
for (size_t i = 0; i < count_below + count_above; ++i)
|
||||||
|
fputs("\033[2K\033[A", stdout);
|
||||||
|
}
|
||||||
|
|
||||||
void Editor::vt_save_cursor()
|
void Editor::vt_save_cursor()
|
||||||
{
|
{
|
||||||
fputs("\033[s", stdout);
|
fputs("\033[s", stdout);
|
||||||
|
@ -442,5 +543,4 @@ void Editor::vt_clear_to_end_of_line()
|
||||||
fputs("\033[K", stdout);
|
fputs("\033[K", stdout);
|
||||||
fflush(stdout);
|
fflush(stdout);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -27,6 +27,7 @@
|
||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
#include <AK/BinarySearch.h>
|
#include <AK/BinarySearch.h>
|
||||||
|
#include <AK/ByteBuffer.h>
|
||||||
#include <AK/FileSystemPath.h>
|
#include <AK/FileSystemPath.h>
|
||||||
#include <AK/Function.h>
|
#include <AK/Function.h>
|
||||||
#include <AK/HashMap.h>
|
#include <AK/HashMap.h>
|
||||||
|
@ -35,6 +36,8 @@
|
||||||
#include <AK/String.h>
|
#include <AK/String.h>
|
||||||
#include <AK/Vector.h>
|
#include <AK/Vector.h>
|
||||||
#include <LibCore/DirIterator.h>
|
#include <LibCore/DirIterator.h>
|
||||||
|
#include <Libraries/LibLine/Span.h>
|
||||||
|
#include <Libraries/LibLine/Style.h>
|
||||||
#include <sys/stat.h>
|
#include <sys/stat.h>
|
||||||
#include <termios.h>
|
#include <termios.h>
|
||||||
|
|
||||||
|
@ -76,8 +79,9 @@ public:
|
||||||
|
|
||||||
void register_character_input_callback(char ch, Function<bool(Editor&)> callback);
|
void register_character_input_callback(char ch, Function<bool(Editor&)> callback);
|
||||||
|
|
||||||
Function<Vector<String>(const String&)> on_tab_complete_first_token = nullptr;
|
Function<Vector<String>(const String&)> on_tab_complete_first_token;
|
||||||
Function<Vector<String>(const String&)> on_tab_complete_other_token = nullptr;
|
Function<Vector<String>(const String&)> on_tab_complete_other_token;
|
||||||
|
Function<void(Editor&)> on_display_refresh;
|
||||||
|
|
||||||
// FIXME: we will have to kindly ask our instantiators to set our signal handlers
|
// FIXME: we will have to kindly ask our instantiators to set our signal handlers
|
||||||
// since we can not do this cleanly ourselves (signal() limitation: cannot give member functions)
|
// since we can not do this cleanly ourselves (signal() limitation: cannot give member functions)
|
||||||
|
@ -92,6 +96,13 @@ public:
|
||||||
void insert(const String&);
|
void insert(const String&);
|
||||||
void insert(const char);
|
void insert(const char);
|
||||||
void cut_mismatching_chars(String& completion, const String& other, size_t start_compare);
|
void cut_mismatching_chars(String& completion, const String& other, size_t start_compare);
|
||||||
|
void stylize(const Span&, const Style&);
|
||||||
|
void strip_styles()
|
||||||
|
{
|
||||||
|
m_spans_starting.clear();
|
||||||
|
m_spans_ending.clear();
|
||||||
|
m_refresh_needed = true;
|
||||||
|
}
|
||||||
|
|
||||||
const struct termios& termios() const { return m_termios; }
|
const struct termios& termios() const { return m_termios; }
|
||||||
const struct termios& default_termios() const { return m_default_termios; }
|
const struct termios& default_termios() const { return m_default_termios; }
|
||||||
|
@ -100,9 +111,37 @@ private:
|
||||||
void vt_save_cursor();
|
void vt_save_cursor();
|
||||||
void vt_restore_cursor();
|
void vt_restore_cursor();
|
||||||
void vt_clear_to_end_of_line();
|
void vt_clear_to_end_of_line();
|
||||||
|
void vt_clear_lines(size_t count_above, size_t count_below = 0);
|
||||||
|
void vt_move_relative(int x, int y);
|
||||||
|
void vt_apply_style(const Style&);
|
||||||
|
|
||||||
|
Style find_applicable_style(size_t offset) const;
|
||||||
|
|
||||||
|
void refresh_display();
|
||||||
|
|
||||||
|
// FIXME: These three will report the wrong value because they do not
|
||||||
|
// take the length of the prompt into consideration, and it does not
|
||||||
|
// appear that we can figure that out easily
|
||||||
|
size_t num_lines() const
|
||||||
|
{
|
||||||
|
return (m_buffer.size() + m_num_columns) / m_num_columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t cursor_line() const
|
||||||
|
{
|
||||||
|
return (m_cursor + m_num_columns) / m_num_columns;
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t offset_in_line() const
|
||||||
|
{
|
||||||
|
auto offset = m_cursor % m_num_columns;
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
Vector<char, 1024> m_buffer;
|
Vector<char, 1024> m_buffer;
|
||||||
|
ByteBuffer m_pending_chars;
|
||||||
size_t m_cursor { 0 };
|
size_t m_cursor { 0 };
|
||||||
|
size_t m_chars_inserted_in_the_middle { 0 };
|
||||||
size_t m_times_tab_pressed { 0 };
|
size_t m_times_tab_pressed { 0 };
|
||||||
size_t m_num_columns { 0 };
|
size_t m_num_columns { 0 };
|
||||||
|
|
||||||
|
@ -126,7 +165,11 @@ private:
|
||||||
};
|
};
|
||||||
InputState m_state { InputState::Free };
|
InputState m_state { InputState::Free };
|
||||||
|
|
||||||
bool m_initialized = false;
|
HashMap<u32, HashMap<u32, Style>> m_spans_starting;
|
||||||
|
HashMap<u32, HashMap<u32, Style>> m_spans_ending;
|
||||||
|
|
||||||
|
bool m_initialized { false };
|
||||||
|
bool m_refresh_needed { false };
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
46
Libraries/LibLine/Span.h
Normal file
46
Libraries/LibLine/Span.h
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, The SerenityOS developers.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
namespace Line {
|
||||||
|
class Span {
|
||||||
|
public:
|
||||||
|
Span(size_t start, size_t end)
|
||||||
|
: m_beginning(start)
|
||||||
|
, m_end(end)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
size_t beginning() const { return m_beginning; }
|
||||||
|
size_t end() const { return m_end; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
size_t m_beginning { 0 };
|
||||||
|
size_t m_end { 0 };
|
||||||
|
};
|
||||||
|
}
|
108
Libraries/LibLine/Style.h
Normal file
108
Libraries/LibLine/Style.h
Normal file
|
@ -0,0 +1,108 @@
|
||||||
|
/*
|
||||||
|
* Copyright (c) 2020, The SerenityOS developers.
|
||||||
|
* All rights reserved.
|
||||||
|
*
|
||||||
|
* Redistribution and use in source and binary forms, with or without
|
||||||
|
* modification, are permitted provided that the following conditions are met:
|
||||||
|
*
|
||||||
|
* 1. Redistributions of source code must retain the above copyright notice, this
|
||||||
|
* list of conditions and the following disclaimer.
|
||||||
|
*
|
||||||
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
||||||
|
* this list of conditions and the following disclaimer in the documentation
|
||||||
|
* and/or other materials provided with the distribution.
|
||||||
|
*
|
||||||
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
||||||
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
||||||
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
||||||
|
* DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
||||||
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
||||||
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
||||||
|
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
||||||
|
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
||||||
|
* OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#pragma once
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
namespace Line {
|
||||||
|
|
||||||
|
class Style {
|
||||||
|
public:
|
||||||
|
enum class Color : int {
|
||||||
|
Default = 9,
|
||||||
|
Black = 0,
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Yellow,
|
||||||
|
Blue,
|
||||||
|
Magenta,
|
||||||
|
Cyan,
|
||||||
|
White,
|
||||||
|
// TODO: it appears that we do not support these SGR options
|
||||||
|
BrightBlack = 60,
|
||||||
|
BrightRed,
|
||||||
|
BrightGreen,
|
||||||
|
BrightYellow,
|
||||||
|
BrightBlue,
|
||||||
|
BrightMagenta,
|
||||||
|
BrightCyan,
|
||||||
|
BrightWhite,
|
||||||
|
};
|
||||||
|
|
||||||
|
struct UnderlineTag {
|
||||||
|
};
|
||||||
|
struct BoldTag {
|
||||||
|
};
|
||||||
|
struct ItalicTag {
|
||||||
|
};
|
||||||
|
struct Background {
|
||||||
|
explicit Background(Color color)
|
||||||
|
: m_color(color)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Color m_color;
|
||||||
|
};
|
||||||
|
struct Foreground {
|
||||||
|
explicit Foreground(Color color)
|
||||||
|
: m_color(color)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
Color m_color;
|
||||||
|
};
|
||||||
|
|
||||||
|
static constexpr UnderlineTag Underline {};
|
||||||
|
static constexpr BoldTag Bold {};
|
||||||
|
static constexpr ItalicTag Italic {};
|
||||||
|
|
||||||
|
// prepare for the horror of templates
|
||||||
|
template <typename T, typename... Rest>
|
||||||
|
Style(const T& style_arg, Rest... rest)
|
||||||
|
: Style(rest...)
|
||||||
|
{
|
||||||
|
set(style_arg);
|
||||||
|
}
|
||||||
|
Style() {}
|
||||||
|
|
||||||
|
bool underline() const { return m_underline; }
|
||||||
|
bool bold() const { return m_bold; }
|
||||||
|
bool italic() const { return m_italic; }
|
||||||
|
Color background() const { return m_background; }
|
||||||
|
Color foreground() const { return m_foreground; }
|
||||||
|
|
||||||
|
void set(const ItalicTag&) { m_italic = true; }
|
||||||
|
void set(const BoldTag&) { m_bold = true; }
|
||||||
|
void set(const UnderlineTag&) { m_underline = true; }
|
||||||
|
void set(const Background& bg) { m_background = bg.m_color; }
|
||||||
|
void set(const Foreground& fg) { m_foreground = fg.m_color; }
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_underline { false };
|
||||||
|
bool m_bold { false };
|
||||||
|
bool m_italic { false };
|
||||||
|
Color m_background { Color::Default };
|
||||||
|
Color m_foreground { Color::Default };
|
||||||
|
};
|
||||||
|
}
|
Loading…
Add table
Add a link
Reference in a new issue