Skip to content

Commit 952df0d

Browse files
committed
Add Terminal Theme Import And Theme Pack
Add support for loading terminal color themes from bundled TOML files and apply the selected theme to new and existing terminal tabs. The builtin terminal pane now discovers theme definitions at startup, lets users choose a theme from the toolbar, persists the selection, and reapplies colors when the application or system theme changes. This also expands the terminal theme catalog substantially by shipping a large set of Alacritty-compatible palette files, and updates the wxTerminalEmulator submodule to a newer revision that supports the new theme handling. * terminal pane * bundled theme assets * wxTerminalEmulator submodule **Generated by CodeLite** Signed-off-by: Eran Ifrah <eran@codelite.org>
1 parent 32cafd4 commit 952df0d

File tree

174 files changed

+6018
-5
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

174 files changed

+6018
-5
lines changed

LiteEditor/app.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,7 +49,7 @@ class CodeLiteApp : public wxApp
4949
void SetParserPaths(const wxArrayString& parserPaths) { this->m_parserPaths = parserPaths; }
5050
const wxArrayString& GetParserPaths() const { return m_parserPaths; }
5151

52-
void MacOpenFile(const wxString& fileName);
52+
void MacOpenFile(const wxString& fileName) override;
5353

5454
void SetStartedInDebuggerMode(bool startedInDebuggerMode) { this->m_startedInDebuggerMode = startedInDebuggerMode; }
5555
bool IsStartedInDebuggerMode() const { return m_startedInDebuggerMode; }

Plugin/CMakeLists.txt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -131,3 +131,5 @@ else()
131131
LIBRARY DESTINATION ${CL_INSTALL_BIN}
132132
ARCHIVE DESTINATION ${CMAKE_INSTALL_PREFIX}/lib)
133133
endif()
134+
135+
codelite_install_terminal_themes()

Plugin/wxTerminalCtrl/clBuiltinTerminalPane.cpp

Lines changed: 207 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,10 +6,12 @@
66
#include "Platform/Platform.hpp"
77
#include "bitmap_loader.h"
88
#include "clFileName.hpp"
9+
#include "clINIParser.hpp"
910
#include "clWorkspaceManager.h"
1011
#include "codelite_events.h"
1112
#include "environmentconfig.h"
1213
#include "event_notifier.h"
14+
#include "file_logger.h"
1315
#include "globals.h"
1416
#include "imanager.h"
1517
#include "macros.h"
@@ -19,6 +21,7 @@
1921

2022
#include <wx/app.h>
2123
#include <wx/choicdlg.h>
24+
#include <wx/dir.h>
2225
#include <wx/frame.h>
2326
#include <wx/sizer.h>
2427
#include <wx/xrc/xmlres.h>
@@ -29,12 +32,18 @@ std::map<wxString, wxString> LocateDefaultTerminals()
2932
{
3033
std::map<wxString, wxString> terminals;
3134
auto bash = ThePlatform->Which("bash");
35+
auto zsh = ThePlatform->Which("zsh");
3236
auto cmd = ThePlatform->Which("powershell");
3337
if (bash.has_value()) {
3438
terminals.insert(
3539
{wxString::Format("%s --login -i", bash.value()), wxString::Format("%s --login -i", bash.value())});
3640
}
3741

42+
if (zsh.has_value()) {
43+
terminals.insert(
44+
{wxString::Format("%s --login -i", zsh.value()), wxString::Format("%s --login -i", zsh.value())});
45+
}
46+
3847
if (cmd.has_value()) {
3948
terminals.insert({cmd.value(), cmd.value()});
4049
}
@@ -62,9 +71,17 @@ clBuiltinTerminalPane::clBuiltinTerminalPane(wxWindow* parent, wxWindowID id)
6271

6372
// Get list of terminals
6473
m_terminal_types = new wxChoice(m_toolbar, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(300), wxNOT_FOUND));
74+
m_terminal_types->SetToolTip(_("Choose shell interpreter"));
6575
UpdateTerminalsChoice(false);
6676
m_toolbar->AddControl(m_terminal_types);
6777

78+
// Get list of terminals
79+
m_choice_themes = new wxChoice(m_toolbar, wxID_ANY, wxDefaultPosition, wxSize(FromDIP(200), wxNOT_FOUND));
80+
m_toolbar->AddControl(m_choice_themes);
81+
m_choice_themes->SetToolTip(_("Choose terminal theme"));
82+
m_choice_themes->Bind(wxEVT_CHOICE, &clBuiltinTerminalPane::OnChoiceTheme, this);
83+
UpdateFont();
84+
6885
#ifdef __WXMSW__
6986
m_toolbar->AddTool(
7087
wxID_REFRESH, _("Scan"), image_list->LoadBitmap("debugger_restart"), wxEmptyString, wxITEM_NORMAL);
@@ -78,12 +95,20 @@ clBuiltinTerminalPane::clBuiltinTerminalPane(wxWindow* parent, wxWindowID id)
7895
GetSizer()->Fit(this);
7996
m_book->Bind(wxEVT_BOOK_PAGE_CHANGED, &clBuiltinTerminalPane::OnPageChanged, this);
8097
EventNotifier::Get()->Bind(wxEVT_WORKSPACE_LOADED, &clBuiltinTerminalPane::OnWorkspaceLoaded, this);
98+
EventNotifier::Get()->Bind(wxEVT_SYS_COLOURS_CHANGED, &clBuiltinTerminalPane::OnThemeChanged, this);
99+
EventNotifier::Get()->Bind(wxEVT_INIT_DONE, &clBuiltinTerminalPane::OnInitDone, this);
100+
101+
#ifdef __WXMAC__
102+
wxTheApp->Bind(wxEVT_MENU, &clBuiltinTerminalPane::OnCopy, this, wxID_COPY);
103+
wxTheApp->Bind(wxEVT_MENU, &clBuiltinTerminalPane::OnPaste, this, wxID_PASTE);
104+
#endif
81105
}
82106

83107
clBuiltinTerminalPane::~clBuiltinTerminalPane()
84108
{
85109
m_book->Unbind(wxEVT_BOOK_PAGE_CHANGED, &clBuiltinTerminalPane::OnPageChanged, this);
86110
EventNotifier::Get()->Unbind(wxEVT_WORKSPACE_LOADED, &clBuiltinTerminalPane::OnWorkspaceLoaded, this);
111+
EventNotifier::Get()->Unbind(wxEVT_SYS_COLOURS_CHANGED, &clBuiltinTerminalPane::OnThemeChanged, this);
87112
clConfig::Get().Write("terminal/last_used_terminal", m_terminal_types->GetStringSelection());
88113
}
89114

@@ -117,6 +142,7 @@ void clBuiltinTerminalPane::OnNew(wxCommandEvent& event)
117142
EnvSetter env_setter{};
118143
std::optional<TerminalView::EnvironmentList> env{std::nullopt};
119144
TerminalView* ctrl = new TerminalView(m_book, cmd, env);
145+
ctrl->SetTheme(m_activeTheme.has_value() ? *m_activeTheme : wxTerminalTheme::MakeDarkTheme());
120146
m_book->AddPage(ctrl, cmd, true);
121147
m_book->SetPageToolTip(m_book->GetPageCount() - 1, cmd);
122148

@@ -148,6 +174,11 @@ void clBuiltinTerminalPane::OnNew(wxCommandEvent& event)
148174
V.push_back(wxAcceleratorEntry{wxACCEL_RAW_CTRL, (int)'A', XRCID("Ctrl_ID_start_of_line")});
149175
V.push_back(wxAcceleratorEntry{wxACCEL_RAW_CTRL, (int)'E', XRCID("Ctrl_ID_end_of_line")});
150176

177+
#ifdef __WXMAC__
178+
V.push_back(wxAcceleratorEntry{wxACCEL_CMD, (int)'V', wxID_PASTE});
179+
V.push_back(wxAcceleratorEntry{wxACCEL_CMD, (int)'C', wxID_COPY});
180+
#endif
181+
151182
wxAcceleratorTable accel_table(V.size(), V.data());
152183
ctrl->SetAcceleratorTable(accel_table);
153184

@@ -162,6 +193,7 @@ void clBuiltinTerminalPane::OnNew(wxCommandEvent& event)
162193
ctrl->Bind(wxEVT_MENU, &clBuiltinTerminalPane::OnAltF, this, XRCID("Alt_ID_forward"));
163194
ctrl->Bind(wxEVT_MENU, &clBuiltinTerminalPane::OnCtrlA, this, XRCID("Ctrl_ID_start_of_line"));
164195
ctrl->Bind(wxEVT_MENU, &clBuiltinTerminalPane::OnCtrlE, this, XRCID("Ctrl_ID_end_of_line"));
196+
ctrl->Bind(wxEVT_MENU, &clBuiltinTerminalPane::OnCtrlE, this, XRCID("Ctrl_ID_end_of_line"));
165197
}
166198

167199
void clBuiltinTerminalPane::OnSetTitle(wxTerminalEvent& event)
@@ -238,7 +270,6 @@ void clBuiltinTerminalPane::WriteTerminalOptionsToDisk(const std::map<wxString,
238270
std::map<wxString, wxString> clBuiltinTerminalPane::GetTerminalsOptions(bool scan)
239271
{
240272
std::map<wxString, wxString> terminals = LocateDefaultTerminals();
241-
;
242273
#ifdef __WXMSW__
243274
if (scan) {
244275
terminals.clear();
@@ -353,6 +384,179 @@ void clBuiltinTerminalPane::OnCtrlE(wxCommandEvent& e)
353384
CHECK_IF_CAN_HANDLE(e);
354385
terminal->SendCtrlE();
355386
}
356-
#undef CHECK_IF_CAN_HANDLE
357387

358-
void clBuiltinTerminalPane::OnInitDone(wxCommandEvent& e) { e.Skip(); }
388+
void clBuiltinTerminalPane::OnInitDone(wxCommandEvent& e)
389+
{
390+
e.Skip();
391+
std::thread([this]() {
392+
wxFileName themes_path{clStandardPaths::Get().GetDataDir(), wxEmptyString};
393+
themes_path.AppendDir("terminal_themes");
394+
themes_path.AppendDir("themes");
395+
wxArrayString theme_files;
396+
wxDir::GetAllFiles(themes_path.GetPath(), &theme_files, "*.toml", wxDIR_FILES);
397+
398+
std::map<wxString, wxTerminalTheme> themes;
399+
for (const wxString& toml_file : theme_files) {
400+
wxFileName fn{toml_file};
401+
auto theme = clBuiltinTerminalPane::FromTOML(fn);
402+
if (theme.has_value()) {
403+
themes.insert({fn.GetName(), *theme});
404+
}
405+
}
406+
407+
{
408+
wxMutexLocker locker(m_themes_mutex);
409+
m_themes.swap(themes);
410+
}
411+
EventNotifier::Get()->RunOnMain<void>([this]() { ThemesUpdated(); });
412+
}).detach();
413+
}
414+
415+
#ifdef __WXMAC__
416+
void clBuiltinTerminalPane::OnCopy(wxCommandEvent& e)
417+
{
418+
CHECK_IF_CAN_HANDLE(e);
419+
terminal->Copy();
420+
}
421+
422+
void clBuiltinTerminalPane::OnPaste(wxCommandEvent& e)
423+
{
424+
CHECK_IF_CAN_HANDLE(e);
425+
terminal->Paste();
426+
}
427+
428+
#endif
429+
430+
void clBuiltinTerminalPane::OnThemeChanged(clCommandEvent& event)
431+
{
432+
event.Skip();
433+
UpdateFont();
434+
ApplyThemeChanges();
435+
}
436+
437+
std::optional<wxTerminalTheme> clBuiltinTerminalPane::FromTOML(const wxFileName& filepath)
438+
{
439+
clDEBUG() << " > Importing Alacritty Theme (TOML) file:" << filepath << endl;
440+
std::string filename = StringUtils::ToStdString(filepath.GetFullPath());
441+
442+
clINIParser ini_parser;
443+
ini_parser.ParseFile(filepath.GetFullPath());
444+
445+
wxTerminalTheme theme;
446+
theme.bg = ini_parser["colors.primary"]["background"].GetValue();
447+
theme.fg = ini_parser["colors.primary"]["foreground"].GetValue();
448+
449+
if (!theme.bg.IsOk() || !theme.fg.IsOk()) {
450+
clSYSTEM() << "Can not import theme:" << filepath << endl;
451+
return std::nullopt;
452+
}
453+
454+
bool is_dark = DrawingUtils::IsDark(theme.bg);
455+
456+
wxString section_name = "colors.normal";
457+
theme.black = ini_parser[section_name]["black"].GetValue();
458+
theme.red = ini_parser[section_name]["red"].GetValue();
459+
theme.green = ini_parser[section_name]["green"].GetValue();
460+
theme.yellow = ini_parser[section_name]["yellow"].GetValue();
461+
theme.blue = ini_parser[section_name]["blue"].GetValue();
462+
theme.magenta = ini_parser[section_name]["magenta"].GetValue();
463+
theme.cyan = ini_parser[section_name]["cyan"].GetValue();
464+
theme.white = ini_parser[section_name]["white"].GetValue();
465+
466+
section_name = "colors.bright";
467+
theme.brightBlack = ini_parser[section_name]["black"].GetValue();
468+
theme.brightRed = ini_parser[section_name]["red"].GetValue();
469+
theme.brightGreen = ini_parser[section_name]["green"].GetValue();
470+
theme.brightYellow = ini_parser[section_name]["yellow"].GetValue();
471+
theme.brightBlue = ini_parser[section_name]["blue"].GetValue();
472+
theme.brightMagenta = ini_parser[section_name]["magenta"].GetValue();
473+
theme.brightCyan = ini_parser[section_name]["cyan"].GetValue();
474+
theme.brightWhite = ini_parser[section_name]["white"].GetValue();
475+
476+
// Optional colours
477+
theme.cursorColour = ini_parser["colors.cursor"]["cursor"].GetValue();
478+
theme.selectionBg = ini_parser["colors.selection"]["background"].GetValue();
479+
theme.highlightBg = ini_parser["colors.search.matches"]["background"].GetValue();
480+
481+
if (!theme.cursorColour.IsOk()) {
482+
theme.cursorColour = is_dark ? *wxYELLOW : *wxBLACK;
483+
}
484+
485+
if (!theme.selectionBg.IsOk()) {
486+
theme.selectionBg = is_dark ? wxT("ORANGE") : wxT("BLUE");
487+
}
488+
489+
if (!theme.highlightBg.IsOk()) {
490+
theme.selectionBg = is_dark ? wxT("GOLD") : wxT("PINK");
491+
}
492+
493+
if (!theme.black.IsOk() || !theme.red.IsOk() || !theme.green.IsOk() || !theme.yellow.IsOk() || !theme.blue.IsOk() ||
494+
!theme.magenta.IsOk() || !theme.cyan.IsOk() || !theme.white.IsOk() || !theme.brightBlack.IsOk() ||
495+
!theme.brightRed.IsOk() || !theme.brightGreen.IsOk() || !theme.brightYellow.IsOk() ||
496+
!theme.brightBlue.IsOk() || !theme.brightMagenta.IsOk() || !theme.brightCyan.IsOk() ||
497+
!theme.brightWhite.IsOk()) {
498+
clSYSTEM() << "failed to read basic colour for theme:" << filepath << endl;
499+
return std::nullopt;
500+
}
501+
return theme;
502+
}
503+
504+
void clBuiltinTerminalPane::ThemesUpdated()
505+
{
506+
m_choice_themes->Clear();
507+
if (m_themes.empty()) {
508+
return;
509+
}
510+
for (const auto& [theme_name, _] : m_themes) {
511+
m_choice_themes->Append(theme_name);
512+
}
513+
514+
wxString selected_theme = m_themes.begin()->first;
515+
selected_theme = clConfig::Get().Read("terminal/theme", selected_theme);
516+
m_choice_themes->SetStringSelection(selected_theme);
517+
518+
if (m_themes.contains(selected_theme)) {
519+
m_activeTheme = m_themes[selected_theme];
520+
} else {
521+
m_activeTheme = wxTerminalTheme::MakeDarkTheme();
522+
}
523+
ApplyThemeChanges();
524+
}
525+
526+
void clBuiltinTerminalPane::OnChoiceTheme(wxCommandEvent& event)
527+
{
528+
wxUnusedVar(event);
529+
wxString selected_theme = m_choice_themes->GetStringSelection();
530+
if (m_themes.contains(selected_theme)) {
531+
m_activeTheme = m_themes[selected_theme];
532+
clConfig::Get().Write("terminal/theme", selected_theme);
533+
clConfig::Get().Save();
534+
} else {
535+
m_activeTheme = wxTerminalTheme::MakeDarkTheme();
536+
}
537+
ApplyThemeChanges();
538+
}
539+
540+
void clBuiltinTerminalPane::ApplyThemeChanges()
541+
{
542+
if (!m_activeTheme.has_value()) {
543+
return;
544+
}
545+
546+
m_activeTheme.value().font = m_activeFont;
547+
548+
for (size_t i = 0; i < m_book->GetPageCount(); ++i) {
549+
auto terminal = dynamic_cast<TerminalView*>(m_book->GetPage(i));
550+
if (terminal) {
551+
terminal->SetTheme(m_activeTheme.value());
552+
}
553+
}
554+
}
555+
556+
void clBuiltinTerminalPane::UpdateFont()
557+
{
558+
auto lexer = ColoursAndFontsManager::Get().GetLexer("text");
559+
if (lexer) {
560+
m_activeFont = lexer->GetFontForStyle(0, this);
561+
}
562+
}

Plugin/wxTerminalCtrl/clBuiltinTerminalPane.hpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,15 +3,21 @@
33

44
#include "Notebook.h"
55
#include "clWorkspaceEvent.hpp"
6+
#include "cl_command_event.h"
67
#include "event_notifier.h"
78
#include "terminal_event.h"
9+
#include "terminal_theme.h"
810

11+
#include <map>
12+
#include <optional>
913
#include <vector>
1014
#include <wx/aui/auibar.h>
1115
#include <wx/choice.h>
1216
#include <wx/panel.h>
17+
#include <wx/thread.h>
1318

1419
class TerminalView;
20+
1521
class WXDLLIMPEXP_SDK clBuiltinTerminalPane : public wxPanel
1622
{
1723
public:
@@ -41,14 +47,30 @@ class WXDLLIMPEXP_SDK clBuiltinTerminalPane : public wxPanel
4147
void OnAltB(wxCommandEvent& e);
4248
void OnCtrlA(wxCommandEvent& e);
4349
void OnCtrlE(wxCommandEvent& e);
50+
#ifdef __WXMAC__
51+
void OnCopy(wxCommandEvent& e);
52+
void OnPaste(wxCommandEvent& e);
53+
#endif
54+
55+
void OnThemeChanged(clCommandEvent& event);
56+
void ThemesUpdated();
57+
void OnChoiceTheme(wxCommandEvent& event);
58+
void ApplyThemeChanges();
59+
void UpdateFont();
4460

4561
private:
62+
static std::optional<wxTerminalTheme> FromTOML(const wxFileName& filepath);
4663
TerminalView* GetActiveTerminal();
4764

4865
wxAuiToolBar* m_toolbar = nullptr;
4966
Notebook* m_book = nullptr;
5067
wxChoice* m_terminal_types = nullptr;
68+
wxChoice* m_choice_themes = nullptr;
5169
std::vector<std::pair<EventFilterCallbackToken, wxEventType>> m_tokens;
70+
wxMutex m_themes_mutex;
71+
std::map<wxString, wxTerminalTheme> m_themes;
72+
std::optional<wxTerminalTheme> m_activeTheme{std::nullopt};
73+
wxFont m_activeFont;
5274
};
5375

5476
#endif // CLBUILTINTERMINALPANE_HPP

0 commit comments

Comments
 (0)