ikalnytskyi/termcolor

Intensity on Windows

keyboardsmoke opened this issue · 6 comments

I feel like there should be a termcolor::intense or something, similar to termcolor::bold, but since there isn't I made this hack to get around the issue

#include "termcolor/termcolor.hpp"

namespace termcolor
{
	namespace _internal
	{
		struct _Intensify
		{
			_Intensify(int color) : color(color) {}

			int color;
		};
	}

	inline _internal::_Intensify intense(int color)
	{
		return _internal::_Intensify(color);
	}
}

template < typename _CharT, typename _Traits>
inline std::basic_ostream<_CharT, _Traits>&
operator<<(std::basic_ostream<_CharT, _Traits>& __os, termcolor::_internal::_Intensify __f)
{
	if (termcolor::_internal::is_colorized(__os))
	{
		termcolor::_internal::win_change_attributes(__os, FOREGROUND_INTENSITY | __f.color);
	}

	return __os;
}

Used like:

std::cerr << termcolor::intense(FOREGROUND_RED) << "You must only use one argument at a time!" << termcolor::reset << std::endl;

Would be nice if this was part of the official repo, I can make a pull request (that isn't a hack) if you'd like.

I'd really like this too, it's an excellent modifier!

IMO, high-intensity colors are a lot better for people with color blindness. The default ANSI ones are severely lacking there.

Sounds like a good thing to add and I'd gladly accept a PR. The only issue I see (and I'm not sure how to work around) is to avoid exposing windows-specific constants such as FOREGROUND_RED. I wonder, could we build an API like this:

std::cerr << termcolor::intense << termcolor::red << "Error << termcolor::reset;

I know, it's a little bit verbose but it hides away windows-specific constants. 🤔

Alternatively, we probably can add 16 constants for base colors, and use them instead, also ensuring that they could be used together with existing termcolor::color<> modifier. WDYT?

I like the idea of exposing the base constants as types, but I think it would be good to go a little further for convenience...

One thing I additionally added in my own attempts at this, to help make the typical usage a little more clear:

namespace termcolor {

    inline
    std::ostream& high_red(std::ostream& stream)
    {
        if (_internal::is_colorized(stream))
        {
    #if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
            stream << "\033[91m";
    #elif defined(TERMCOLOR_OS_WINDOWS)
            _internal::win_change_attributes(stream,
                FOREGROUND_RED | FOREGROUND_INTENSITY
            );
    #endif
        }
        return stream;
    }

    ...
}

I think this is probably a good set of public functions to include (regardless of how they would be implemented), because it becomes a lot more obvious looking at this, that calling any such function will invalidate the previous foreground color if any had been set using something like os << termcolor::red, and the ability to do this is exposed at the same level and in the same terms, as the existing ostream base colors.

You could probably make such public functions call whatever you want, but adding them seems to help with clarity.

The way ANSI hacked high colors in really looks like they just shoved more colors beside the existing ones:

\033[31m // red
vs
\033[91m // high_red

This doesn't feel like a modifier in my eyes, and you wouldn't be able to promote non-base colors to intense colors for this reason.

I just realized there is a similar use case for background colors.

It's basically done in the same way.

    inline
    std::ostream& on_high_red(std::ostream& stream)
    {
        if (_internal::is_colorized(stream))
        {
    #if defined(TERMCOLOR_OS_MACOS) || defined(TERMCOLOR_OS_LINUX)
            stream << "\033[101m";
    #elif defined(TERMCOLOR_OS_WINDOWS)
            _internal::win_change_attributes(stream, -1,
                BACKGROUND_RED | BACKGROUND_INTENSITY
            );
    #endif
        }
        return stream;
    }
    ...
}

ANSI:

\033[41m // red
vs
\033[101m // high_red

I like the solution suggested by @yuri-sevatz in his PR. I believe it will be accepted soon.

PR #53 has been landed.