mirror of
https://github.com/RGBCube/serenity
synced 2025-07-26 10:27:34 +00:00
LibVT+Terminal: Add color scheme support
This commit introduces color scheme support to Terminal. These are found in `/res/terminal_colors` and the default color scheme can be set in `~/.config/Terminal.ini`. Furthermore, a combo box is added for setting the color scheme at runtime. The previously used default color scheme has been added to `/res/terminal-colors/Default.ini`. To make the implementation more compatible with other color schemes, `TerminalWidget` now supports overriding the default foreground and background colors.
This commit is contained in:
parent
99033876ec
commit
acbd1d14d0
6 changed files with 128 additions and 5 deletions
|
@ -3,3 +3,4 @@ Command=
|
||||||
[Window]
|
[Window]
|
||||||
Opacity=255
|
Opacity=255
|
||||||
Bell=Visible
|
Bell=Visible
|
||||||
|
ColorScheme=Default
|
||||||
|
|
23
Base/res/terminal-colors/Default.ini
Normal file
23
Base/res/terminal-colors/Default.ini
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
[Primary]
|
||||||
|
Background=#000000
|
||||||
|
Foreground=#ffffff
|
||||||
|
|
||||||
|
[Normal]
|
||||||
|
Black=#000000
|
||||||
|
Red=#cc0000
|
||||||
|
Green=#3e9a06
|
||||||
|
Yellow=#c4a000
|
||||||
|
Blue=#3465a4
|
||||||
|
Magenta=#75507b
|
||||||
|
Cyan=#06989a
|
||||||
|
White=#eeeec
|
||||||
|
|
||||||
|
[Bright]
|
||||||
|
Black=#555753
|
||||||
|
Red=#ef2929
|
||||||
|
Green=#8ae234
|
||||||
|
Yellow=#fce94f
|
||||||
|
Blue=#729fcf
|
||||||
|
Magenta=#ad7fa8
|
||||||
|
Cyan=#34e2e2
|
||||||
|
White=#ffffff
|
|
@ -60,4 +60,17 @@
|
||||||
orientation: "Horizontal"
|
orientation: "Horizontal"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@GUI::GroupBox {
|
||||||
|
title: "Color scheme"
|
||||||
|
shrink_to_fit: true
|
||||||
|
|
||||||
|
layout: @GUI::VerticalBoxLayout {
|
||||||
|
margins: [6, 16, 6, 6]
|
||||||
|
}
|
||||||
|
|
||||||
|
@GUI::ComboBox {
|
||||||
|
name: "color_scheme_combo"
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,10 +4,12 @@
|
||||||
* SPDX-License-Identifier: BSD-2-Clause
|
* SPDX-License-Identifier: BSD-2-Clause
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
#include <AK/QuickSort.h>
|
||||||
#include <AK/URL.h>
|
#include <AK/URL.h>
|
||||||
#include <Applications/Terminal/TerminalSettingsWindowGML.h>
|
#include <Applications/Terminal/TerminalSettingsWindowGML.h>
|
||||||
#include <LibCore/ArgsParser.h>
|
#include <LibCore/ArgsParser.h>
|
||||||
#include <LibCore/ConfigFile.h>
|
#include <LibCore/ConfigFile.h>
|
||||||
|
#include <LibCore/DirIterator.h>
|
||||||
#include <LibCore/File.h>
|
#include <LibCore/File.h>
|
||||||
#include <LibDesktop/Launcher.h>
|
#include <LibDesktop/Launcher.h>
|
||||||
#include <LibGUI/Action.h>
|
#include <LibGUI/Action.h>
|
||||||
|
@ -16,9 +18,11 @@
|
||||||
#include <LibGUI/BoxLayout.h>
|
#include <LibGUI/BoxLayout.h>
|
||||||
#include <LibGUI/Button.h>
|
#include <LibGUI/Button.h>
|
||||||
#include <LibGUI/CheckBox.h>
|
#include <LibGUI/CheckBox.h>
|
||||||
|
#include <LibGUI/ComboBox.h>
|
||||||
#include <LibGUI/Event.h>
|
#include <LibGUI/Event.h>
|
||||||
#include <LibGUI/FontPicker.h>
|
#include <LibGUI/FontPicker.h>
|
||||||
#include <LibGUI/Icon.h>
|
#include <LibGUI/Icon.h>
|
||||||
|
#include <LibGUI/ItemListModel.h>
|
||||||
#include <LibGUI/Menu.h>
|
#include <LibGUI/Menu.h>
|
||||||
#include <LibGUI/Menubar.h>
|
#include <LibGUI/Menubar.h>
|
||||||
#include <LibGUI/OpacitySlider.h>
|
#include <LibGUI/OpacitySlider.h>
|
||||||
|
@ -105,7 +109,7 @@ static RefPtr<GUI::Window> create_settings_window(VT::TerminalWidget& terminal)
|
||||||
window->set_window_type(GUI::WindowType::ToolWindow);
|
window->set_window_type(GUI::WindowType::ToolWindow);
|
||||||
window->set_title("Terminal settings");
|
window->set_title("Terminal settings");
|
||||||
window->set_resizable(false);
|
window->set_resizable(false);
|
||||||
window->resize(200, 210);
|
window->resize(200, 240);
|
||||||
window->center_within(*terminal.window());
|
window->center_within(*terminal.window());
|
||||||
|
|
||||||
auto& settings = window->set_main_widget<GUI::Widget>();
|
auto& settings = window->set_main_widget<GUI::Widget>();
|
||||||
|
@ -149,6 +153,25 @@ static RefPtr<GUI::Window> create_settings_window(VT::TerminalWidget& terminal)
|
||||||
terminal.set_max_history_size(value);
|
terminal.set_max_history_size(value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// The settings window takes a reference to this vector, so it needs to outlive this scope.
|
||||||
|
// As long as we ensure that only one settings window may be open at a time (which we do),
|
||||||
|
// this should cause no problems.
|
||||||
|
static Vector<String> color_scheme_names;
|
||||||
|
color_scheme_names.clear();
|
||||||
|
Core::DirIterator iterator("/res/terminal-colors", Core::DirIterator::SkipParentAndBaseDir);
|
||||||
|
while (iterator.has_next()) {
|
||||||
|
auto path = iterator.next_path();
|
||||||
|
path.replace(".ini", "");
|
||||||
|
color_scheme_names.append(path);
|
||||||
|
}
|
||||||
|
quick_sort(color_scheme_names);
|
||||||
|
auto& color_scheme_combo = *settings.find_descendant_of_type_named<GUI::ComboBox>("color_scheme_combo");
|
||||||
|
color_scheme_combo.set_only_allow_values_from_model(true);
|
||||||
|
color_scheme_combo.set_model(*GUI::ItemListModel<String>::create(color_scheme_names));
|
||||||
|
color_scheme_combo.set_selected_index(color_scheme_names.find_first_index(terminal.color_scheme_name()).value());
|
||||||
|
color_scheme_combo.on_change = [&](auto&, const GUI::ModelIndex& index) {
|
||||||
|
terminal.set_color_scheme(index.data().as_string());
|
||||||
|
};
|
||||||
return window;
|
return window;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -79,6 +79,9 @@ TerminalWidget::TerminalWidget(int ptm_fd, bool automatic_size_policy, RefPtr<Co
|
||||||
, m_automatic_size_policy(automatic_size_policy)
|
, m_automatic_size_policy(automatic_size_policy)
|
||||||
, m_config(move(config))
|
, m_config(move(config))
|
||||||
{
|
{
|
||||||
|
static_assert(sizeof(m_colors) == sizeof(xterm_colors));
|
||||||
|
memcpy(m_colors, xterm_colors, sizeof(m_colors));
|
||||||
|
|
||||||
set_override_cursor(Gfx::StandardCursor::IBeam);
|
set_override_cursor(Gfx::StandardCursor::IBeam);
|
||||||
set_focus_policy(GUI::FocusPolicy::StrongFocus);
|
set_focus_policy(GUI::FocusPolicy::StrongFocus);
|
||||||
set_accepts_emoji_input(true);
|
set_accepts_emoji_input(true);
|
||||||
|
@ -147,6 +150,8 @@ TerminalWidget::TerminalWidget(int ptm_fd, bool automatic_size_policy, RefPtr<Co
|
||||||
|
|
||||||
update_copy_action();
|
update_copy_action();
|
||||||
update_paste_action();
|
update_paste_action();
|
||||||
|
|
||||||
|
set_color_scheme(m_config->read_entry("Window", "ColorScheme", "Default"));
|
||||||
}
|
}
|
||||||
|
|
||||||
TerminalWidget::~TerminalWidget()
|
TerminalWidget::~TerminalWidget()
|
||||||
|
@ -1135,6 +1140,54 @@ void TerminalWidget::update_paste_action()
|
||||||
m_paste_action->set_enabled(GUI::Clipboard::the().mime_type().starts_with("text/") && !GUI::Clipboard::the().data().is_empty());
|
m_paste_action->set_enabled(GUI::Clipboard::the().mime_type().starts_with("text/") && !GUI::Clipboard::the().data().is_empty());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void TerminalWidget::set_color_scheme(const StringView& name)
|
||||||
|
{
|
||||||
|
if (name.contains('/')) {
|
||||||
|
dbgln("Shenanigans! Color scheme names can't contain slashes.");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_color_scheme_name = name;
|
||||||
|
|
||||||
|
constexpr StringView color_names[] = {
|
||||||
|
"Black",
|
||||||
|
"Red",
|
||||||
|
"Green",
|
||||||
|
"Yellow",
|
||||||
|
"Blue",
|
||||||
|
"Magenta",
|
||||||
|
"Cyan",
|
||||||
|
"White"
|
||||||
|
};
|
||||||
|
|
||||||
|
auto color_config = Core::ConfigFile::open(String::formatted("/res/terminal-colors/{}.ini", name));
|
||||||
|
|
||||||
|
auto default_background = Gfx::Color::from_string(color_config->read_entry("Primary", "Background"));
|
||||||
|
if (default_background.has_value())
|
||||||
|
m_default_background_color = default_background.value();
|
||||||
|
else
|
||||||
|
m_default_background_color = Gfx::Color::from_rgb(m_colors[(u8)VT::Color::ANSIColor::Black]);
|
||||||
|
|
||||||
|
auto default_foreground = Gfx::Color::from_string(color_config->read_entry("Primary", "Foreground"));
|
||||||
|
if (default_foreground.has_value())
|
||||||
|
m_default_foreground_color = default_foreground.value();
|
||||||
|
else
|
||||||
|
m_default_foreground_color = Gfx::Color::from_rgb(m_colors[(u8)VT::Color::ANSIColor::White]);
|
||||||
|
|
||||||
|
for (u8 color_idx = 0; color_idx < 8; ++color_idx) {
|
||||||
|
auto rgb = Gfx::Color::from_string(color_config->read_entry("Normal", color_names[color_idx]));
|
||||||
|
if (rgb.has_value())
|
||||||
|
m_colors[color_idx] = rgb.value().value();
|
||||||
|
}
|
||||||
|
|
||||||
|
for (u8 color_idx = 0; color_idx < 8; ++color_idx) {
|
||||||
|
auto rgb = Gfx::Color::from_string(color_config->read_entry("Bright", color_names[color_idx]));
|
||||||
|
if (rgb.has_value())
|
||||||
|
m_colors[color_idx + 8] = rgb.value().value();
|
||||||
|
}
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
Gfx::IntSize TerminalWidget::widget_size_for_font(const Gfx::Font& font) const
|
Gfx::IntSize TerminalWidget::widget_size_for_font(const Gfx::Font& font) const
|
||||||
{
|
{
|
||||||
return {
|
return {
|
||||||
|
@ -1149,15 +1202,15 @@ Gfx::Color TerminalWidget::terminal_color_to_rgb(VT::Color color)
|
||||||
case VT::Color::Kind::RGB:
|
case VT::Color::Kind::RGB:
|
||||||
return Gfx::Color::from_rgb(color.as_rgb());
|
return Gfx::Color::from_rgb(color.as_rgb());
|
||||||
case VT::Color::Kind::Indexed:
|
case VT::Color::Kind::Indexed:
|
||||||
return Gfx::Color::from_rgb(xterm_colors[color.as_indexed()]);
|
return Gfx::Color::from_rgb(m_colors[color.as_indexed()]);
|
||||||
case VT::Color::Kind::Named: {
|
case VT::Color::Kind::Named: {
|
||||||
auto ansi = color.as_named();
|
auto ansi = color.as_named();
|
||||||
if ((u16)ansi < 256)
|
if ((u16)ansi < 256)
|
||||||
return Gfx::Color::from_rgb(xterm_colors[(u16)ansi]);
|
return Gfx::Color::from_rgb(m_colors[(u16)ansi]);
|
||||||
else if (ansi == VT::Color::ANSIColor::DefaultForeground)
|
else if (ansi == VT::Color::ANSIColor::DefaultForeground)
|
||||||
return Gfx::Color::from_rgb(xterm_colors[7]);
|
return m_default_foreground_color;
|
||||||
else if (ansi == VT::Color::ANSIColor::DefaultBackground)
|
else if (ansi == VT::Color::ANSIColor::DefaultBackground)
|
||||||
return Gfx::Color::from_rgb(xterm_colors[0]);
|
return m_default_background_color;
|
||||||
else
|
else
|
||||||
VERIFY_NOT_REACHED();
|
VERIFY_NOT_REACHED();
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,6 +81,8 @@ public:
|
||||||
void paste();
|
void paste();
|
||||||
void clear_including_history();
|
void clear_including_history();
|
||||||
|
|
||||||
|
const StringView color_scheme_name() const { return m_color_scheme_name; }
|
||||||
|
|
||||||
Function<void(const StringView&)> on_title_change;
|
Function<void(const StringView&)> on_title_change;
|
||||||
Function<void(const Gfx::IntSize&)> on_terminal_size_change;
|
Function<void(const Gfx::IntSize&)> on_terminal_size_change;
|
||||||
Function<void()> on_command_exit;
|
Function<void()> on_command_exit;
|
||||||
|
@ -91,6 +93,8 @@ public:
|
||||||
|
|
||||||
void set_font_and_resize_to_fit(const Gfx::Font&);
|
void set_font_and_resize_to_fit(const Gfx::Font&);
|
||||||
|
|
||||||
|
void set_color_scheme(const StringView&);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// ^GUI::Widget
|
// ^GUI::Widget
|
||||||
virtual void event(Core::Event&) override;
|
virtual void event(Core::Event&) override;
|
||||||
|
@ -157,6 +161,12 @@ private:
|
||||||
// Snapshot of m_hovered_href when opening a context menu for a hyperlink.
|
// Snapshot of m_hovered_href when opening a context menu for a hyperlink.
|
||||||
String m_context_menu_href;
|
String m_context_menu_href;
|
||||||
|
|
||||||
|
unsigned m_colors[256];
|
||||||
|
Gfx::Color m_default_foreground_color;
|
||||||
|
Gfx::Color m_default_background_color;
|
||||||
|
|
||||||
|
String m_color_scheme_name;
|
||||||
|
|
||||||
BellMode m_bell_mode { BellMode::Visible };
|
BellMode m_bell_mode { BellMode::Visible };
|
||||||
bool m_alt_key_held { false };
|
bool m_alt_key_held { false };
|
||||||
bool m_rectangle_selection { false };
|
bool m_rectangle_selection { false };
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue