diff --git a/doc/config b/doc/config index d315765f..a870cac5 100644 --- a/doc/config +++ b/doc/config @@ -503,11 +503,10 @@ ## progressbar_color, progressbar_elapsed_color, ## alternative_ui_separator_color. ## -## Note: due to technical limitations of ncurses, if 256 colors -## are used, it is possible to either use only the colors with -## default background color, or all pairs from 1_1 up to 254_127, -## depending on the ncurses version used. -## +## Note: due to technical limitations of older ncurses +## versionw, if 256 colors are used there is a possibility +## that you'll be able to use only colors with transparent +## background. # #colors_enabled = yes # diff --git a/src/curses/menu_impl.h b/src/curses/menu_impl.h index 451886b7..e94f97d3 100644 --- a/src/curses/menu_impl.h +++ b/src/curses/menu_impl.h @@ -217,11 +217,11 @@ void Menu::refresh() *this << Format::Reverse; *this << m_highlight_color; } - mvwhline(m_window, line, 0, NC::Key::Space, m_width); if ((*m_items)[m_drawn_position].isSelected()) *this << m_selected_prefix; if (m_item_displayer) m_item_displayer(*this); + *this << NC::TermManip::ClearToEOL; if ((*m_items)[m_drawn_position].isSelected()) *this << m_selected_suffix; if (m_highlight_enabled && m_drawn_position == m_highlight) diff --git a/src/curses/window.cpp b/src/curses/window.cpp index b4c7a05c..93ca3a87 100644 --- a/src/curses/window.cpp +++ b/src/curses/window.cpp @@ -187,6 +187,10 @@ int add_base() } } + +int color_pair_counter; +std::vector color_pair_map; + } namespace NC { @@ -195,42 +199,56 @@ const short Color::transparent = -1; const short Color::previous = -2; Color Color::Default(0, 0, true, false); -Color Color::Black(COLOR_BLACK, Color::transparent); -Color Color::Red(COLOR_RED, Color::transparent); -Color Color::Green(COLOR_GREEN, Color::transparent); -Color Color::Yellow(COLOR_YELLOW, Color::transparent); -Color Color::Blue(COLOR_BLUE, Color::transparent); -Color Color::Magenta(COLOR_MAGENTA, Color::transparent); -Color Color::Cyan(COLOR_CYAN, Color::transparent); -Color Color::White(COLOR_WHITE, Color::transparent); +Color Color::Black(COLOR_BLACK, Color::previous); +Color Color::Red(COLOR_RED, Color::previous); +Color Color::Green(COLOR_GREEN, Color::previous); +Color Color::Yellow(COLOR_YELLOW, Color::previous); +Color Color::Blue(COLOR_BLUE, Color::previous); +Color Color::Magenta(COLOR_MAGENTA, Color::previous); +Color Color::Cyan(COLOR_CYAN, Color::previous); +Color Color::White(COLOR_WHITE, Color::previous); Color Color::End(0, 0, false, true); int Color::pairNumber() const { - int result; - if (isDefault()) - result = 0; - else if (previousBackground()) - throw std::logic_error("color depends on the previous background value"); - else if (isEnd()) + int result = 0; + if (isEnd()) throw std::logic_error("'end' doesn't have a corresponding pair number"); - else + else if (!isDefault()) { - // colors start with 0, but pairs start with 1. additionally - // first pairs are for transparent background, which has a - // value of -1, so we need to add 1 to both foreground and - // background value. - result = background() + 1; - result *= COLORS; - result += foreground() + 1; + if (!previousBackground()) + result = background() + 1; + result *= 256; + result += foreground(); + + assert(result < int(color_pair_map.size())); + + // NCurses allows for a limited number of color pairs to be registered, so + // in order to be able to support all the combinations we want to, we need + // to dynamically register only pairs of colors we're actually using. + if (!color_pair_map[result]) + { + // Check if there are any unused pairs left and either register the one + // that was requested or return a default one if there is no space left. + if (color_pair_counter >= COLOR_PAIRS) + result = 0; + else + { + init_pair(color_pair_counter, foreground(), background()); + color_pair_map[result] = color_pair_counter; + ++color_pair_counter; + } + } + result = color_pair_map[result]; } return result; } std::istream &operator>>(std::istream &is, Color &c) { + const short invalid_color_value = -1337; auto get_single_color = [](const std::string &s, bool background) { - short result = -1; + short result = invalid_color_value; if (s == "black") result = COLOR_BLACK; else if (s == "red") @@ -247,13 +265,15 @@ std::istream &operator>>(std::istream &is, Color &c) result = COLOR_CYAN; else if (s == "white") result = COLOR_WHITE; + else if (background && s == "transparent") + result = NC::Color::transparent; else if (background && s == "previous") result = NC::Color::previous; else if (std::all_of(s.begin(), s.end(), isdigit)) { result = atoi(s.c_str()); - if (result < 1 || result > 256) - result = -1; + if (result < (background ? 0 : 1) || result > 256) + result = invalid_color_value; else --result; } @@ -276,7 +296,7 @@ std::istream &operator>>(std::istream &is, Color &c) else { short fg = get_single_color(sc, false); - if (fg == -1) + if (fg == invalid_color_value) is.setstate(std::ios::failbit); // Check if there is background color else if (!is.eof() && is.peek() == '_') @@ -284,13 +304,13 @@ std::istream &operator>>(std::istream &is, Color &c) is.get(); sc = get_color(is); short bg = get_single_color(sc, true); - if (bg == -1) + if (bg == invalid_color_value) is.setstate(std::ios::failbit); else c = Color(fg, bg); } else - c = Color(fg, NC::Color::transparent); + c = Color(fg, NC::Color::previous); } return is; } @@ -365,11 +385,16 @@ void initScreen(bool enable_colors, bool enable_mouse) { start_color(); use_default_colors(); - int npair = 1; - for (int bg = -1; bg < COLORS; ++bg) + color_pair_map.resize(256 * 256, 0); + + // Predefine pairs for colors with transparent background, all the other + // ones will be dynamically registered in Color::pairNumber when they're + // used. + color_pair_counter = 1; + for (int fg = 0; fg < COLORS; ++fg, ++color_pair_counter) { - for (int fg = 0; npair < COLOR_PAIRS && fg < COLORS; ++fg, ++npair) - init_pair(npair, fg, bg); + init_pair(color_pair_counter, fg, -1); + color_pair_map[fg] = color_pair_counter; } } raw(); @@ -418,36 +443,29 @@ void destroyScreen() endwin(); } -Window::Window(size_t startx, - size_t starty, - size_t width, - size_t height, - std::string title, - Color color, - Border border) - : m_window(nullptr), - m_start_x(startx), - m_start_y(starty), - m_width(width), - m_height(height), - m_window_timeout(-1), - m_color(color), - m_base_color(color), - m_border(std::move(border)), - m_prompt_hook(0), - m_title(std::move(title)), - m_escape_terminal_sequences(true), - m_bold_counter(0), - m_underline_counter(0), - m_reverse_counter(0), - m_alt_charset_counter(0) +Window::Window(size_t startx, size_t starty, size_t width, size_t height, + std::string title, Color color, Border border) + : m_window(nullptr), + m_start_x(startx), + m_start_y(starty), + m_width(width), + m_height(height), + m_window_timeout(-1), + m_border(std::move(border)), + m_prompt_hook(0), + m_title(std::move(title)), + m_escape_terminal_sequences(true), + m_bold_counter(0), + m_underline_counter(0), + m_reverse_counter(0), + m_alt_charset_counter(0) { if (m_start_x > size_t(COLS) - || m_start_y > size_t(LINES) - || m_width+m_start_x > size_t(COLS) - || m_height+m_start_y > size_t(LINES)) + || m_start_y > size_t(LINES) + || m_width+m_start_x > size_t(COLS) + || m_height+m_start_y > size_t(LINES)) throw std::logic_error("constructed window doesn't fit into the terminal"); - + if (m_border) { ++m_start_x; @@ -462,8 +480,9 @@ Window::Window(size_t startx, } m_window = newpad(m_height, m_width); - - setColor(m_color); + + setBaseColor(color); + setColor(m_base_color); } Window::Window(const Window &rhs) @@ -548,8 +567,7 @@ void Window::setColor(Color c) c = m_base_color; if (c != Color::Default) { - if (c.previousBackground()) - c = Color(c.foreground(), m_color.background()); + assert(!c.previousBackground()); wcolor_set(m_window, c.pairNumber(), nullptr); } else @@ -557,9 +575,12 @@ void Window::setColor(Color c) m_color = std::move(c); } -void Window::setBaseColor(Color c) +void Window::setBaseColor(const Color &color) { - m_base_color = std::move(c); + if (color.previousBackground()) + m_base_color = Color(color.foreground(), Color::transparent); + else + m_base_color = color; } void Window::setBorder(Border border) @@ -1290,8 +1311,20 @@ Window &Window::operator<<(const Color &c) } else { - setColor(c); - m_color_stack.push(c); + if (c.previousBackground()) + { + short background = m_color.isDefault() + ? Color::transparent + : m_color.background(); + Color cc = Color(c.foreground(), background); + setColor(cc); + m_color_stack.push(cc); + } + else + { + setColor(c); + m_color_stack.push(c); + } } return *this; } diff --git a/src/curses/window.h b/src/curses/window.h index 534afad5..413e563a 100644 --- a/src/curses/window.h +++ b/src/curses/window.h @@ -354,7 +354,7 @@ struct Window /// Sets window's base color /// @param fg foregound base color /// @param bg background base color - void setBaseColor(Color c); + void setBaseColor(const Color &color); /// Sets window's border /// @param border new window's border