From acbd1d14d0ca8ea063a9acaec988913557acd22b Mon Sep 17 00:00:00 2001 From: Daniel Bertalan Date: Fri, 28 May 2021 21:26:39 +0200 Subject: [PATCH] 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. --- Base/home/anon/.config/Terminal.ini | 1 + Base/res/terminal-colors/Default.ini | 23 +++++++ .../Terminal/TerminalSettingsWindow.gml | 13 ++++ Userland/Applications/Terminal/main.cpp | 25 +++++++- Userland/Libraries/LibVT/TerminalWidget.cpp | 61 +++++++++++++++++-- Userland/Libraries/LibVT/TerminalWidget.h | 10 +++ 6 files changed, 128 insertions(+), 5 deletions(-) create mode 100644 Base/res/terminal-colors/Default.ini diff --git a/Base/home/anon/.config/Terminal.ini b/Base/home/anon/.config/Terminal.ini index 3cdf124fb5..25a254a2db 100644 --- a/Base/home/anon/.config/Terminal.ini +++ b/Base/home/anon/.config/Terminal.ini @@ -3,3 +3,4 @@ Command= [Window] Opacity=255 Bell=Visible +ColorScheme=Default diff --git a/Base/res/terminal-colors/Default.ini b/Base/res/terminal-colors/Default.ini new file mode 100644 index 0000000000..e5a9b73860 --- /dev/null +++ b/Base/res/terminal-colors/Default.ini @@ -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 diff --git a/Userland/Applications/Terminal/TerminalSettingsWindow.gml b/Userland/Applications/Terminal/TerminalSettingsWindow.gml index 07b8400c56..6c906fd463 100644 --- a/Userland/Applications/Terminal/TerminalSettingsWindow.gml +++ b/Userland/Applications/Terminal/TerminalSettingsWindow.gml @@ -60,4 +60,17 @@ 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" + } + } } diff --git a/Userland/Applications/Terminal/main.cpp b/Userland/Applications/Terminal/main.cpp index 846b0ffdd2..dd8fc9e325 100644 --- a/Userland/Applications/Terminal/main.cpp +++ b/Userland/Applications/Terminal/main.cpp @@ -4,10 +4,12 @@ * SPDX-License-Identifier: BSD-2-Clause */ +#include #include #include #include #include +#include #include #include #include @@ -16,9 +18,11 @@ #include #include #include +#include #include #include #include +#include #include #include #include @@ -105,7 +109,7 @@ static RefPtr create_settings_window(VT::TerminalWidget& terminal) window->set_window_type(GUI::WindowType::ToolWindow); window->set_title("Terminal settings"); window->set_resizable(false); - window->resize(200, 210); + window->resize(200, 240); window->center_within(*terminal.window()); auto& settings = window->set_main_widget(); @@ -149,6 +153,25 @@ static RefPtr create_settings_window(VT::TerminalWidget& terminal) 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 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("color_scheme_combo"); + color_scheme_combo.set_only_allow_values_from_model(true); + color_scheme_combo.set_model(*GUI::ItemListModel::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; } diff --git a/Userland/Libraries/LibVT/TerminalWidget.cpp b/Userland/Libraries/LibVT/TerminalWidget.cpp index 6998a8a96d..f010e8ed69 100644 --- a/Userland/Libraries/LibVT/TerminalWidget.cpp +++ b/Userland/Libraries/LibVT/TerminalWidget.cpp @@ -79,6 +79,9 @@ TerminalWidget::TerminalWidget(int ptm_fd, bool automatic_size_policy, RefPtrread_entry("Window", "ColorScheme", "Default")); } 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()); } +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 { return { @@ -1149,15 +1202,15 @@ Gfx::Color TerminalWidget::terminal_color_to_rgb(VT::Color color) case VT::Color::Kind::RGB: return Gfx::Color::from_rgb(color.as_rgb()); 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: { auto ansi = color.as_named(); 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) - return Gfx::Color::from_rgb(xterm_colors[7]); + return m_default_foreground_color; else if (ansi == VT::Color::ANSIColor::DefaultBackground) - return Gfx::Color::from_rgb(xterm_colors[0]); + return m_default_background_color; else VERIFY_NOT_REACHED(); } diff --git a/Userland/Libraries/LibVT/TerminalWidget.h b/Userland/Libraries/LibVT/TerminalWidget.h index 39d8a7f2e9..5d4307e651 100644 --- a/Userland/Libraries/LibVT/TerminalWidget.h +++ b/Userland/Libraries/LibVT/TerminalWidget.h @@ -81,6 +81,8 @@ public: void paste(); void clear_including_history(); + const StringView color_scheme_name() const { return m_color_scheme_name; } + Function on_title_change; Function on_terminal_size_change; Function on_command_exit; @@ -91,6 +93,8 @@ public: void set_font_and_resize_to_fit(const Gfx::Font&); + void set_color_scheme(const StringView&); + private: // ^GUI::Widget virtual void event(Core::Event&) override; @@ -157,6 +161,12 @@ private: // Snapshot of m_hovered_href when opening a context menu for a hyperlink. 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 }; bool m_alt_key_held { false }; bool m_rectangle_selection { false };