From 1b370545d7f2051a6a89189505242cfdd69cbd4b Mon Sep 17 00:00:00 2001 From: Jessica Hamilton Date: Sun, 18 Dec 2022 06:26:04 +0000 Subject: [PATCH] Terminal: graphical settings for configuring themes. Implements #16835: Make colour schemes modular. Implements #15936: Terminal settings should allow to drop a colour. Change-Id: I1071c9285ba887e734af47b724f9de7f6c660581 Reviewed-on: https://review.haiku-os.org/c/haiku/+/5950 Tested-by: Commit checker robot Reviewed-by: Adrien Destugues --- src/apps/terminal/AppearPrefView.cpp | 219 ----------- src/apps/terminal/AppearPrefView.h | 14 - src/apps/terminal/ColorListView.cpp | 148 +++++++ src/apps/terminal/ColorListView.h | 31 ++ src/apps/terminal/ColorPreview.cpp | 284 ++++++++++++++ src/apps/terminal/ColorPreview.h | 51 +++ src/apps/terminal/Jamfile | 4 + src/apps/terminal/PrefWindow.h | 8 +- src/apps/terminal/TermConst.h | 2 + src/apps/terminal/TermWindow.cpp | 20 +- src/apps/terminal/TermWindow.h | 2 + src/apps/terminal/ThemeView.cpp | 554 +++++++++++++++++++++++++++ src/apps/terminal/ThemeView.h | 91 +++++ src/apps/terminal/ThemeWindow.cpp | 205 ++++++++++ src/apps/terminal/ThemeWindow.h | 50 +++ 15 files changed, 1444 insertions(+), 239 deletions(-) create mode 100644 src/apps/terminal/ColorListView.cpp create mode 100644 src/apps/terminal/ColorListView.h create mode 100644 src/apps/terminal/ColorPreview.cpp create mode 100644 src/apps/terminal/ColorPreview.h create mode 100644 src/apps/terminal/ThemeView.cpp create mode 100644 src/apps/terminal/ThemeView.h create mode 100644 src/apps/terminal/ThemeWindow.cpp create mode 100644 src/apps/terminal/ThemeWindow.h diff --git a/src/apps/terminal/AppearPrefView.cpp b/src/apps/terminal/AppearPrefView.cpp index 256df2d7c4..596f35a04c 100644 --- a/src/apps/terminal/AppearPrefView.cpp +++ b/src/apps/terminal/AppearPrefView.cpp @@ -46,34 +46,6 @@ AppearancePrefView::AppearancePrefView(const char* name, BGroupView(name, B_VERTICAL, 5), fTerminalMessenger(messenger) { - const char* kColorTable[] = { - B_TRANSLATE_MARK("Text"), - B_TRANSLATE_MARK("Background"), - B_TRANSLATE_MARK("Cursor"), - B_TRANSLATE_MARK("Text under cursor"), - B_TRANSLATE_MARK("Selected text"), - B_TRANSLATE_MARK("Selected background"), - "", - B_TRANSLATE_MARK("ANSI black color"), - B_TRANSLATE_MARK("ANSI red color"), - B_TRANSLATE_MARK("ANSI green color"), - B_TRANSLATE_MARK("ANSI yellow color"), - B_TRANSLATE_MARK("ANSI blue color"), - B_TRANSLATE_MARK("ANSI magenta color"), - B_TRANSLATE_MARK("ANSI cyan color"), - B_TRANSLATE_MARK("ANSI white color"), - "", - B_TRANSLATE_MARK("ANSI bright black color"), - B_TRANSLATE_MARK("ANSI bright red color"), - B_TRANSLATE_MARK("ANSI bright green color"), - B_TRANSLATE_MARK("ANSI bright yellow color"), - B_TRANSLATE_MARK("ANSI bright blue color"), - B_TRANSLATE_MARK("ANSI bright magenta color"), - B_TRANSLATE_MARK("ANSI bright cyan color"), - B_TRANSLATE_MARK("ANSI bright white color"), - NULL - }; - fBlinkCursor = new BCheckBox( B_TRANSLATE("Blinking cursor"), new BMessage(MSG_BLINK_CURSOR_CHANGED)); @@ -110,16 +82,6 @@ AppearancePrefView::AppearancePrefView(const char* name, } fEncodingField = new BMenuField(B_TRANSLATE("Encoding:"), encodingMenu); - BPopUpMenu* schemesPopUp = _MakeColorSchemeMenu(MSG_COLOR_SCHEME_CHANGED, - gColorSchemes); - fColorSchemeField = new BMenuField(B_TRANSLATE("Color scheme:"), - schemesPopUp); - - BPopUpMenu* colorsPopUp = _MakeMenu(MSG_COLOR_FIELD_CHANGED, kColorTable, - kColorTable[0]); - - fColorField = new BMenuField(B_TRANSLATE("Color:"), colorsPopUp); - fTabTitle = new BTextControl("tabTitle", B_TRANSLATE("Tab title:"), "", NULL); fTabTitle->SetModificationMessage( @@ -151,14 +113,8 @@ AppearancePrefView::AppearancePrefView(const char* name, .Add(fFontField->CreateMenuBarLayoutItem(), 1, 3) .Add(fEncodingField->CreateLabelLayoutItem(), 0, 4) .Add(fEncodingField->CreateMenuBarLayoutItem(), 1, 4) - .Add(fColorSchemeField->CreateLabelLayoutItem(), 0, 5) - .Add(fColorSchemeField->CreateMenuBarLayoutItem(), 1, 5) - .Add(fColorField->CreateLabelLayoutItem(), 0, 6) - .Add(fColorField->CreateMenuBarLayoutItem(), 1, 6) .End() .AddGlue() - .Add(fColorControl = new BColorControl(BPoint(10, 10), - B_CELLS_32x8, 8.0, "", new BMessage(MSG_COLOR_CHANGED))) .Add(fBlinkCursor) .Add(fAllowBold) .Add(fUseOptionAsMetaKey) @@ -169,18 +125,8 @@ AppearancePrefView::AppearancePrefView(const char* name, fFontField->SetAlignment(B_ALIGN_RIGHT); fWindowSizeField->SetAlignment(B_ALIGN_RIGHT); fEncodingField->SetAlignment(B_ALIGN_RIGHT); - fColorField->SetAlignment(B_ALIGN_RIGHT); - fColorSchemeField->SetAlignment(B_ALIGN_RIGHT); Revert(); - - BTextControl* redInput = (BTextControl*)fColorControl->ChildAt(0); - BTextControl* greenInput = (BTextControl*)fColorControl->ChildAt(1); - BTextControl* blueInput = (BTextControl*)fColorControl->ChildAt(2); - - redInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); - greenInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); - blueInput->SetAlignment(B_ALIGN_RIGHT, B_ALIGN_LEFT); } @@ -197,10 +143,8 @@ AppearancePrefView::Revert() fUseOptionAsMetaKey->SetValue(pref->getBool(PREF_USE_OPTION_AS_META)); fWarnOnExit->SetValue(pref->getBool(PREF_WARN_ON_EXIT)); - _SetCurrentColorScheme(); _SetEncoding(pref->getString(PREF_TEXT_ENCODING)); _SetWindowSize(pref->getInt32(PREF_ROWS), pref->getInt32(PREF_COLS)); - fColorControl->SetValue(pref->getRGB(PREF_TEXT_FORE_COLOR)); const char* family = pref->getString(PREF_HALF_FONT_FAMILY); const char* style = pref->getString(PREF_HALF_FONT_STYLE); @@ -229,13 +173,8 @@ AppearancePrefView::AttachedToWindow() fontSizeMenu->SetTargetForItems(this); } - fColorControl->SetTarget(this); - fColorField->Menu()->SetTargetForItems(this); - fColorSchemeField->Menu()->SetTargetForItems(this); fWindowSizeField->Menu()->SetTargetForItems(this); fEncodingField->Menu()->SetTargetForItems(this); - - _SetCurrentColorScheme(); } @@ -277,55 +216,6 @@ AppearancePrefView::MessageReceived(BMessage* msg) break; } - case MSG_COLOR_CHANGED: - { - const BMessage* itemMessage - = fColorField->Menu()->FindMarked()->Message(); - const char* label = NULL; - if (itemMessage->FindString("label", &label) != B_OK) - break; - rgb_color oldColor = PrefHandler::Default()->getRGB(label); - if (oldColor != fColorControl->ValueAsColor()) { - BMenuItem* item = fColorSchemeField->Menu()->FindMarked(); - if (strcmp(item->Label(), gCustomColorScheme.name) != 0) { - item->SetMarked(false); - item = fColorSchemeField->Menu()->FindItem( - gCustomColorScheme.name); - if (item) - item->SetMarked(true); - } - - PrefHandler::Default()->setRGB(label, - fColorControl->ValueAsColor()); - modified = true; - } - break; - } - - case MSG_COLOR_SCHEME_CHANGED: - { - color_scheme* newScheme = NULL; - if (msg->FindPointer("color_scheme", - (void**)&newScheme) == B_OK) { - _ChangeColorScheme(newScheme); - const char* label = NULL; - if (fColorField->Menu()->FindMarked()->Message()->FindString( - "label", &label) == B_OK) - fColorControl->SetValue( - PrefHandler::Default()->getRGB(label)); - modified = true; - } - break; - } - - case MSG_COLOR_FIELD_CHANGED: - { - const char* label = NULL; - if (msg->FindString("label", &label) == B_OK) - fColorControl->SetValue(PrefHandler::Default()->getRGB(label)); - break; - } - case MSG_COLS_CHANGED: { int rows = msg->FindInt32("rows"); @@ -417,70 +307,6 @@ AppearancePrefView::MessageReceived(BMessage* msg) } -void -AppearancePrefView::_ChangeColorScheme(color_scheme* scheme) -{ - PrefHandler* pref = PrefHandler::Default(); - - pref->setRGB(PREF_TEXT_FORE_COLOR, scheme->text_fore_color); - pref->setRGB(PREF_TEXT_BACK_COLOR, scheme->text_back_color); - pref->setRGB(PREF_SELECT_FORE_COLOR, scheme->select_fore_color); - pref->setRGB(PREF_SELECT_BACK_COLOR, scheme->select_back_color); - pref->setRGB(PREF_CURSOR_FORE_COLOR, scheme->cursor_fore_color); - pref->setRGB(PREF_CURSOR_BACK_COLOR, scheme->cursor_back_color); - pref->setRGB(PREF_ANSI_BLACK_COLOR, scheme->ansi_colors.black); - pref->setRGB(PREF_ANSI_RED_COLOR, scheme->ansi_colors.red); - pref->setRGB(PREF_ANSI_GREEN_COLOR, scheme->ansi_colors.green); - pref->setRGB(PREF_ANSI_YELLOW_COLOR, scheme->ansi_colors.yellow); - pref->setRGB(PREF_ANSI_BLUE_COLOR, scheme->ansi_colors.blue); - pref->setRGB(PREF_ANSI_MAGENTA_COLOR, scheme->ansi_colors.magenta); - pref->setRGB(PREF_ANSI_CYAN_COLOR, scheme->ansi_colors.cyan); - pref->setRGB(PREF_ANSI_WHITE_COLOR, scheme->ansi_colors.white); - pref->setRGB(PREF_ANSI_BLACK_HCOLOR, scheme->ansi_colors_h.black); - pref->setRGB(PREF_ANSI_RED_HCOLOR, scheme->ansi_colors_h.red); - pref->setRGB(PREF_ANSI_GREEN_HCOLOR, scheme->ansi_colors_h.green); - pref->setRGB(PREF_ANSI_YELLOW_HCOLOR, scheme->ansi_colors_h.yellow); - pref->setRGB(PREF_ANSI_BLUE_HCOLOR, scheme->ansi_colors_h.blue); - pref->setRGB(PREF_ANSI_MAGENTA_HCOLOR, scheme->ansi_colors_h.magenta); - pref->setRGB(PREF_ANSI_CYAN_HCOLOR, scheme->ansi_colors_h.cyan); - pref->setRGB(PREF_ANSI_WHITE_HCOLOR, scheme->ansi_colors_h.white); -} - - -void -AppearancePrefView::_SetCurrentColorScheme() -{ - PrefHandler* pref = PrefHandler::Default(); - - pref->LoadColorScheme(&gCustomColorScheme); - - const char* currentSchemeName = NULL; - - int32 i = 0; - while (i < gColorSchemes->CountItems()) { - const color_scheme *item = gColorSchemes->ItemAt(i); - i++; - - if (gCustomColorScheme == *item) { - currentSchemeName = item->name; - break; - } - } - - // If the scheme is not one of the known ones, assume a custom one. - if (currentSchemeName == NULL) - currentSchemeName = "Custom"; - - for (int32 i = 0; i < fColorSchemeField->Menu()->CountItems(); i++) { - BMenuItem* item = fColorSchemeField->Menu()->ItemAt(i); - if (strcmp(item->Label(), currentSchemeName) == 0) { - item->SetMarked(true); - break; - } - } -} - - void AppearancePrefView::_SetEncoding(const char* name) { @@ -633,51 +459,6 @@ AppearancePrefView::_MakeMenu(uint32 msg, const char** items, } -/*static*/ void -AppearancePrefView::_MakeColorSchemeMenuItem(uint32 msg, const color_scheme *item, - BPopUpMenu *menu) -{ - if (item == NULL) - return; - - BMessage* message = new BMessage(msg); - message->AddPointer("color_scheme", (const void*)item); - menu->AddItem(new BMenuItem(item->name, message)); -} - - -/*static*/ BPopUpMenu* -AppearancePrefView::_MakeColorSchemeMenu(uint32 msg, BObjectList *items) -{ - BPopUpMenu* menu = new BPopUpMenu(""); - - FindColorSchemeByName comparator("Default"); - - const color_scheme* defaultItem = items->FindIf(comparator); - - _MakeColorSchemeMenuItem(msg, defaultItem, menu); - - int32 i = 0; - while (i < items->CountItems()) { - const color_scheme *item = items->ItemAt(i); - i++; - - if (strcmp(item->name, "") == 0) - menu->AddSeparatorItem(); - else if (item == defaultItem) - continue; - else - _MakeColorSchemeMenuItem(msg, item, menu); - } - - // Add the custom item at the very end - menu->AddSeparatorItem(); - _MakeColorSchemeMenuItem(msg, &gCustomColorScheme, menu); - - return menu; -} - - void AppearancePrefView::_MarkSelectedFont(const char* family, const char* style, const char* size) diff --git a/src/apps/terminal/AppearPrefView.h b/src/apps/terminal/AppearPrefView.h index 95630491ba..d40fa526d1 100644 --- a/src/apps/terminal/AppearPrefView.h +++ b/src/apps/terminal/AppearPrefView.h @@ -18,9 +18,7 @@ static const uint32 MSG_HALF_FONT_CHANGED = 'mchf'; static const uint32 MSG_HALF_SIZE_CHANGED = 'mchs'; static const uint32 MSG_FULL_FONT_CHANGED = 'mcff'; static const uint32 MSG_FULL_SIZE_CHANGED = 'mcfs'; -static const uint32 MSG_COLOR_FIELD_CHANGED = 'mccf'; static const uint32 MSG_COLOR_CHANGED = 'mcbc'; -static const uint32 MSG_COLOR_SCHEME_CHANGED = 'mccs'; static const uint32 MSG_TAB_TITLE_SETTING_CHANGED = 'mtts'; static const uint32 MSG_WINDOW_TITLE_SETTING_CHANGED = 'mwts'; @@ -54,8 +52,6 @@ public: virtual void AttachedToWindow(); private: - void _ChangeColorScheme(color_scheme* scheme); - void _SetCurrentColorScheme(); void _SetEncoding(const char* encoding); void _SetWindowSize(int rows, int cols); @@ -71,12 +67,6 @@ private: static BPopUpMenu* _MakeMenu(uint32 msg, const char** items, const char* defaultItem); - static BPopUpMenu* _MakeColorSchemeMenu(uint32 msg, - BObjectList *items); - static void _MakeColorSchemeMenuItem(uint32 msg, - const color_scheme *item, - BPopUpMenu* menu); - void _MarkSelectedFont(const char* family, const char* style, const char* size); @@ -89,10 +79,6 @@ private: BMenuField* fWindowSizeField; BMenuField* fEncodingField; - BMenuField* fColorSchemeField; - BMenuField* fColorField; - BColorControl* fColorControl; - BTextControl* fTabTitle; BTextControl* fWindowTitle; diff --git a/src/apps/terminal/ColorListView.cpp b/src/apps/terminal/ColorListView.cpp new file mode 100644 index 0000000000..9c51de01a8 --- /dev/null +++ b/src/apps/terminal/ColorListView.cpp @@ -0,0 +1,148 @@ +/* + * Copyright 2016-2022 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * John Scipione, jscipione@gmail.com + */ + + +#include "ColorListView.h" + +#include + +#include +#include + +#include +#include + +#include "ColorItem.h" +#include "ThemeView.h" + + +// golden ratio +#ifdef M_PHI +# undef M_PHI +#endif +#define M_PHI 1.61803398874989484820 + + +// #pragma mark - ColorListView + + +ColorListView::ColorListView(const char* name, + list_view_type type, uint32 flags) + : + BListView(name, type, flags) +{ +} + + +ColorListView::~ColorListView() +{ +} + + +bool +ColorListView::InitiateDrag(BPoint where, int32 index, bool wasSelected) +{ + ColorItem* colorItem + = dynamic_cast(ItemAt(index)); + if (colorItem == NULL) + return false; + + rgb_color color = colorItem->Color(); + + BString hexStr; + hexStr.SetToFormat("#%.2X%.2X%.2X", color.red, color.green, color.blue); + + BMessage message(B_PASTE); + message.AddData("text/plain", B_MIME_TYPE, hexStr.String(), + hexStr.Length()); + message.AddData(kRGBColor, B_RGB_COLOR_TYPE, &color, sizeof(color)); + + float itemHeight = colorItem->Height() - 5; + BRect rect(0.0f, 0.0f, roundf(itemHeight * M_PHI) - 1, itemHeight - 1); + + BBitmap* bitmap = new BBitmap(rect, B_RGB32, true); + if (bitmap->Lock()) { + BView* view = new BView(rect, "", B_FOLLOW_NONE, B_WILL_DRAW); + bitmap->AddChild(view); + + view->SetHighColor(B_TRANSPARENT_COLOR); + view->FillRect(view->Bounds()); + + ++rect.top; + ++rect.left; + + view->SetHighColor(0, 0, 0, 100); + view->FillRect(rect); + rect.OffsetBy(-1.0f, -1.0f); + + view->SetHighColor(std::min(255, (int)(1.2 * color.red + 40)), + std::min(255, (int)(1.2 * color.green + 40)), + std::min(255, (int)(1.2 * color.blue + 40))); + view->StrokeRect(rect); + + ++rect.left; + ++rect.top; + + view->SetHighColor((int32)(0.8 * color.red), + (int32)(0.8 * color.green), + (int32)(0.8 * color.blue)); + view->StrokeRect(rect); + + --rect.right; + --rect.bottom; + + view->SetHighColor(color.red, color.green, color.blue); + view->FillRect(rect); + view->Sync(); + + bitmap->Unlock(); + } + + DragMessage(&message, bitmap, B_OP_ALPHA, BPoint(14.0f, 14.0f)); + + return true; +} + + +void +ColorListView::MessageReceived(BMessage* message) +{ + // if we received a dropped message, see if it contains color data + if (message->WasDropped()) { + BPoint dropPoint = message->DropPoint(); + ConvertFromScreen(&dropPoint); + int32 index = IndexOf(dropPoint); + ColorItem* item = dynamic_cast(ItemAt(index)); + rgb_color* color; + ssize_t size; + if (item != NULL && message->FindData(kRGBColor, B_RGB_COLOR_TYPE, + (const void**)&color, &size) == B_OK) { + // build message to send to ThemeView + uint32 command = index == CurrentSelection() + ? MSG_SET_CURRENT_COLOR : MSG_SET_COLOR; + BMessage setColorMessage = BMessage(command); + setColorMessage.AddData(kRGBColor, B_RGB_COLOR_TYPE, color, size); + // if setting different color, add which color to set + if (command == MSG_SET_COLOR) + setColorMessage.AddString(kName, item->Text()); + + // build messenger and send message + BMessenger messenger = BMessenger(Parent()); + if (messenger.IsValid()) { + messenger.SendMessage(&setColorMessage); + if (command == MSG_SET_COLOR) { + // redraw item, MSG_SET_CURRENT_COLOR does this for us + item->SetColor(*color); + InvalidateItem(index); + } + } + } + } + + BListView::MessageReceived(message); +} diff --git a/src/apps/terminal/ColorListView.h b/src/apps/terminal/ColorListView.h new file mode 100644 index 0000000000..d57f0da076 --- /dev/null +++ b/src/apps/terminal/ColorListView.h @@ -0,0 +1,31 @@ +/* + * Copyright 2016-2022 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * John Scipione, jscipione@gmail.com + */ +#ifndef COLORWHICH_LIST_VIEW_H +#define COLORWHICH_LIST_VIEW_H + + +#include + + +class ColorListView : public BListView +{ +public: + ColorListView(const char* name, + list_view_type type + = B_SINGLE_SELECTION_LIST, + uint32 flags = B_WILL_DRAW + | B_FRAME_EVENTS | B_NAVIGABLE); + virtual ~ColorListView(); + + virtual bool InitiateDrag(BPoint where, int32 index, + bool wasSelected); + virtual void MessageReceived(BMessage* message); +}; + + +#endif // COLORWHICH_LIST_VIEW_H diff --git a/src/apps/terminal/ColorPreview.cpp b/src/apps/terminal/ColorPreview.cpp new file mode 100644 index 0000000000..98c88a586e --- /dev/null +++ b/src/apps/terminal/ColorPreview.cpp @@ -0,0 +1,284 @@ +/* + * Copyright 2002-2022 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm, darkwyrm@earthlink.net + * John Scipione, jscipione@gmail.com + */ + + +#include "ColorPreview.h" + +#include + +#include + +#include +#include +#include +#include +#include +#include + +#include "ThemeView.h" +#include "TermConst.h" + + +static const int32 kMsgMessageRunner = 'MsgR'; + + +// #pragma mark - ColorPreview + + +ColorPreview::ColorPreview(BMessage* message, uint32 flags) + : + BControl("ColorPreview", "", message, flags | B_WILL_DRAW), + fColor(ui_color(B_PANEL_BACKGROUND_COLOR)), + fDisabledColor((rgb_color){ 128, 128, 128 }), + fMessageRunner(NULL), + fIsRectangle(true) +{ + SetViewColor(B_TRANSPARENT_COLOR); + SetLowColor(0, 0, 0); + SetExplicitSize(BSize(StringWidth("M") * 8, StringWidth("M") * 7)); +} + + +ColorPreview::~ColorPreview(void) +{ +} + + +void +ColorPreview::Draw(BRect updateRect) +{ + rgb_color color; + if (IsEnabled()) + color = fColor; + else + color = fDisabledColor; + + if (fIsRectangle) { + if (IsEnabled()) { + rgb_color background = ui_color(B_PANEL_BACKGROUND_COLOR); + rgb_color shadow = tint_color(background, B_DARKEN_1_TINT); + rgb_color darkShadow = tint_color(background, B_DARKEN_3_TINT); + rgb_color light = tint_color(background, B_LIGHTEN_MAX_TINT); + + BRect bounds(Bounds()); + + BeginLineArray(4); + AddLine(BPoint(bounds.left, bounds.bottom), + BPoint(bounds.left, bounds.top), shadow); + AddLine(BPoint(bounds.left + 1.0, bounds.top), + BPoint(bounds.right, bounds.top), shadow); + AddLine(BPoint(bounds.right, bounds.top + 1.0), + BPoint(bounds.right, bounds.bottom), light); + AddLine(BPoint(bounds.right - 1.0, bounds.bottom), + BPoint(bounds.left + 1.0, bounds.bottom), light); + EndLineArray(); + bounds.InsetBy(1.0, 1.0); + + BeginLineArray(4); + AddLine(BPoint(bounds.left, bounds.bottom), + BPoint(bounds.left, bounds.top), darkShadow); + AddLine(BPoint(bounds.left + 1.0, bounds.top), + BPoint(bounds.right, bounds.top), darkShadow); + AddLine(BPoint(bounds.right, bounds.top + 1.0), + BPoint(bounds.right, bounds.bottom), background); + AddLine(BPoint(bounds.right - 1.0, bounds.bottom), + BPoint(bounds.left + 1.0, bounds.bottom), background); + EndLineArray(); + bounds.InsetBy(1.0, 1.0); + + SetHighColor(color); + FillRect(bounds); + } else { + SetHighColor(color); + FillRect(Bounds()); + } + } else { + // fill background + SetHighColor(ui_color(B_PANEL_BACKGROUND_COLOR)); + FillRect(updateRect); + + SetHighColor(color); + FillEllipse(Bounds()); + + if (IsEnabled()) + StrokeEllipse(Bounds(), B_SOLID_LOW); + } +} + + +void +ColorPreview::MessageReceived(BMessage* message) +{ + // If we received a dropped message, see if it contains color data + if (message->WasDropped()) { + rgb_color* color; + ssize_t size; + if (message->FindData(kRGBColor, B_RGB_COLOR_TYPE, + (const void**)&color, &size) == B_OK) { + BMessage setColorMessage(MSG_SET_CURRENT_COLOR); + setColorMessage.AddData(kRGBColor, B_RGB_COLOR_TYPE, color, + sizeof(rgb_color)); + Invoke(&setColorMessage); + } + } else if ((int32)message->what == kMsgMessageRunner) { + BPoint where; + uint32 buttons; + GetMouse(&where, &buttons); + + _DragColor(where); + } + + BControl::MessageReceived(message); +} + + +void +ColorPreview::MouseDown(BPoint where) +{ + BWindow* window = Window(); + if (window != NULL) + window->Activate(); + + fMessageRunner = new BMessageRunner(this, new BMessage(kMsgMessageRunner), + 300000, 1); + + SetMouseEventMask(B_POINTER_EVENTS, + B_SUSPEND_VIEW_FOCUS | B_LOCK_WINDOW_FOCUS); + + BRect rect = Bounds().InsetByCopy(2.0f, 2.0f); + rect.top = roundf(rect.bottom / 2.0f + 1); + + if (rect.Contains(where)) { + Invalidate(); + Invoke(); + } + + BControl::MouseDown(where); +} + + +void +ColorPreview::MouseMoved(BPoint where, uint32 transit, const BMessage* message) +{ + if (fMessageRunner != NULL) + _DragColor(where); + + BControl::MouseMoved(where, transit, message); +} + + +void +ColorPreview::MouseUp(BPoint where) +{ + delete fMessageRunner; + fMessageRunner = NULL; + + BControl::MouseUp(where); +} + + +rgb_color +ColorPreview::Color(void) const +{ + return fColor; +} + + +void +ColorPreview::SetColor(rgb_color color) +{ + color.alpha = 255; + + SetHighColor(color); + fColor = color; + + Invalidate(); + Invoke(); +} + + +void +ColorPreview::SetColor(uint8 red, uint8 green, uint8 blue) +{ + SetHighColor(red, green, blue); + fColor.red = red; + fColor.green = green; + fColor.blue = blue; + fColor.alpha = 255; + + Invalidate(); + Invoke(); +} + + +void +ColorPreview::SetMode(bool rectangle) +{ + fIsRectangle = rectangle; +} + + +// #pragma mark - ColorPreview private methods + + +void +ColorPreview::_DragColor(BPoint where) +{ + BString hexStr; + hexStr.SetToFormat("#%.2X%.2X%.2X", fColor.red, fColor.green, fColor.blue); + + BMessage message(B_PASTE); + message.AddData("text/plain", B_MIME_TYPE, hexStr.String(), + hexStr.Length()); + message.AddData(kRGBColor, B_RGB_COLOR_TYPE, &fColor, sizeof(fColor)); + + BRect rect(0.0f, 0.0f, 20.0f, 20.0f); + + BBitmap* bitmap = new BBitmap(rect, B_RGB32, true); + if (bitmap->Lock()) { + BView* view = new BView(rect, "", B_FOLLOW_NONE, B_WILL_DRAW); + bitmap->AddChild(view); + + view->SetHighColor(B_TRANSPARENT_COLOR); + view->FillRect(view->Bounds()); + + ++rect.top; + ++rect.left; + + view->SetHighColor(0, 0, 0, 100); + view->FillRect(rect); + rect.OffsetBy(-1.0f, -1.0f); + + view->SetHighColor(std::min(255, (int)(1.2 * fColor.red + 40)), + std::min(255, (int)(1.2 * fColor.green + 40)), + std::min(255, (int)(1.2 * fColor.blue + 40))); + view->StrokeRect(rect); + + ++rect.left; + ++rect.top; + + view->SetHighColor((int32)(0.8 * fColor.red), + (int32)(0.8 * fColor.green), + (int32)(0.8 * fColor.blue)); + view->StrokeRect(rect); + + --rect.right; + --rect.bottom; + + view->SetHighColor(fColor.red, fColor.green, fColor.blue); + view->FillRect(rect); + view->Sync(); + + bitmap->Unlock(); + } + + DragMessage(&message, bitmap, B_OP_ALPHA, BPoint(14.0f, 14.0f)); + + MouseUp(where); +} diff --git a/src/apps/terminal/ColorPreview.h b/src/apps/terminal/ColorPreview.h new file mode 100644 index 0000000000..3794b25c3a --- /dev/null +++ b/src/apps/terminal/ColorPreview.h @@ -0,0 +1,51 @@ +/* + * Copyright 2002-2022 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + * + * Authors: + * DarkWyrm, darkwyrm@earthlink.net + * John Scipione, jscipione@gmail.com + */ +#ifndef COLOR_PREVIEW_H_ +#define COLOR_PREVIEW_H_ + + +#include + + +class BMessage; +class BMessageRunner; + + +class ColorPreview : public BControl +{ +public: + ColorPreview(BMessage* message, + uint32 flags = B_WILL_DRAW); + ~ColorPreview(void); + + virtual void Draw(BRect updateRect); + virtual void MessageReceived(BMessage* message); + virtual void MouseDown(BPoint where); + virtual void MouseMoved(BPoint where, uint32 transit, + const BMessage* message); + virtual void MouseUp(BPoint where); + + rgb_color Color(void) const; + void SetColor(rgb_color color); + void SetColor(uint8 r, uint8 g, uint8 b); + + void SetMode(bool rectangle); + +private: + void _DragColor(BPoint where); + + rgb_color fColor; + rgb_color fDisabledColor; + + BMessageRunner* fMessageRunner; + + bool fIsRectangle; +}; + +#endif // COLOR_PREVIEW_H_ diff --git a/src/apps/terminal/Jamfile b/src/apps/terminal/Jamfile index fcd89343a9..4a6916e401 100644 --- a/src/apps/terminal/Jamfile +++ b/src/apps/terminal/Jamfile @@ -11,6 +11,8 @@ Application Terminal : Arguments.cpp BasicTerminalBuffer.cpp Colors.cpp + ColorListView.cpp + ColorPreview.cpp FindWindow.cpp Globals.cpp HistoryBuffer.cpp @@ -35,6 +37,8 @@ Application Terminal : TermViewHighlight.cpp TermViewStates.cpp TermWindow.cpp + ThemeView.cpp + ThemeWindow.cpp TitlePlaceholderMapper.cpp VTKeyTbl.c VTPrsTbl.c diff --git a/src/apps/terminal/PrefWindow.h b/src/apps/terminal/PrefWindow.h index 69e82b88a5..3cee56454d 100644 --- a/src/apps/terminal/PrefWindow.h +++ b/src/apps/terminal/PrefWindow.h @@ -14,10 +14,10 @@ #include // local messages -const ulong MSG_DEFAULTS_PRESSED = 'defl'; -const ulong MSG_SAVEAS_PRESSED = 'canl'; -const ulong MSG_REVERT_PRESSED = 'revt'; -const ulong MSG_PREF_CLOSED = 'mspc'; +const uint32 MSG_DEFAULTS_PRESSED = 'defl'; +const uint32 MSG_SAVEAS_PRESSED = 'canl'; +const uint32 MSG_REVERT_PRESSED = 'revt'; +const uint32 MSG_PREF_CLOSED = 'mspc'; class BButton; class BFilePanel; diff --git a/src/apps/terminal/TermConst.h b/src/apps/terminal/TermConst.h index 72532df888..f704a156a5 100644 --- a/src/apps/terminal/TermConst.h +++ b/src/apps/terminal/TermConst.h @@ -16,6 +16,7 @@ // Application signature (Must same in Terminal.rdef) #define TERM_SIGNATURE "application/x-vnd.Haiku-Terminal" #define PREFFILE_MIMETYPE "text/x-terminal-pref" +#define THEMEFILE_MIMETYPE "text/x-terminal-theme" // Signature of R5's Terminal. Needed for proper drop-in window count support #define R5_TERM_SIGNATURE "application/x-vnd.Be-SHEL" @@ -52,6 +53,7 @@ static const uint32 MENU_FIND_STRING = 'Mfpr'; static const uint32 MENU_FIND_NEXT = 'Mfnx'; static const uint32 MENU_FIND_PREVIOUS = 'Mfbw'; static const uint32 MENU_SHOW_COLOR = 'Mcol'; +static const uint32 MENU_THEME_OPEN = 'Mthm'; static const uint32 M_GET_DEVICE_NUM = 'Mgdn'; diff --git a/src/apps/terminal/TermWindow.cpp b/src/apps/terminal/TermWindow.cpp index 1ffe0db88b..4fbc48a790 100644 --- a/src/apps/terminal/TermWindow.cpp +++ b/src/apps/terminal/TermWindow.cpp @@ -1,5 +1,5 @@ /* - * Copyright 2007-2015, Haiku, Inc. All rights reserved. + * Copyright 2007-2022, Haiku, Inc. All rights reserved. * Copyright (c) 2004 Daniel Furrer * Copyright (c) 2003-2004 Kian Duffy * Copyright (C) 1998,99 Kazuho Okui and Takashi Murai. @@ -67,6 +67,8 @@ #include "ShellParameters.h" #include "TermConst.h" #include "TermScrollView.h" +#include "ThemeWindow.h" +#include "ThemeView.h" #include "TitlePlaceholderMapper.h" @@ -541,6 +543,7 @@ TermWindow::_SetupMenu() .AddSeparator() .AddItem(B_TRANSLATE("Settings" B_UTF8_ELLIPSIS), MENU_PREF_OPEN, ',') + .AddItem(B_TRANSLATE("Theme settings" B_UTF8_ELLIPSIS), MENU_THEME_OPEN) .End(); AddChild(fMenuBar); @@ -768,6 +771,17 @@ TermWindow::MessageReceived(BMessage *message) fPrefWindow = NULL; break; + case MENU_THEME_OPEN: + if (!fThemeWindow) + fThemeWindow = new ThemeWindow(this); + else + fThemeWindow->Activate(); + break; + + case MSG_THEME_CLOSED: + fThemeWindow = NULL; + break; + case MSG_WINDOW_TITLE_SETTING_CHANGED: case MSG_TAB_TITLE_SETTING_CHANGED: _TitleSettingsChanged(); @@ -987,8 +1001,10 @@ TermWindow::MessageReceived(BMessage *message) PostMessage(MSG_HALF_FONT_CHANGED); break; - case MSG_COLOR_CHANGED: case MSG_COLOR_SCHEME_CHANGED: + case MSG_SET_CURRENT_COLOR: + case MSG_SET_COLOR: + case MSG_UPDATE_COLOR: { for (int32 i = fTabView->CountTabs() - 1; i >= 0; i--) { TermViewContainerView* container = _TermViewContainerViewAt(i); diff --git a/src/apps/terminal/TermWindow.h b/src/apps/terminal/TermWindow.h index 26d55edf79..d92475148f 100644 --- a/src/apps/terminal/TermWindow.h +++ b/src/apps/terminal/TermWindow.h @@ -51,6 +51,7 @@ class BMenuBar; class FindWindow; class PrefWindow; class TermViewContainerView; +class ThemeWindow; class TermWindow : public BWindow, private SmartTabView::Listener, @@ -211,6 +212,7 @@ private: BMessage* fPrintSettings; PrefWindow* fPrefWindow; + ThemeWindow* fThemeWindow; FindWindow* fFindPanel; BRect fSavedFrame; window_look fSavedLook; diff --git a/src/apps/terminal/ThemeView.cpp b/src/apps/terminal/ThemeView.cpp new file mode 100644 index 0000000000..0fdc416171 --- /dev/null +++ b/src/apps/terminal/ThemeView.cpp @@ -0,0 +1,554 @@ +/* + * Copyright 2022 Haiku, Inc. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "ThemeView.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "ThemeWindow.h" +#include "Colors.h" +#include "ColorPreview.h" +#include "ColorListView.h" +#include "ColorItem.h" +#include "TermConst.h" +#include "PrefHandler.h" + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "Terminal ThemeView" + +#define COLOR_DROPPED 'cldp' +#define DECORATOR_CHANGED 'dcch' + + +/* static */ const char* +ThemeView::kColorTable[] = { + B_TRANSLATE_MARK("Text"), + B_TRANSLATE_MARK("Background"), + B_TRANSLATE_MARK("Cursor"), + B_TRANSLATE_MARK("Text under cursor"), + B_TRANSLATE_MARK("Selected text"), + B_TRANSLATE_MARK("Selected background"), + B_TRANSLATE_MARK("ANSI black color"), + B_TRANSLATE_MARK("ANSI red color"), + B_TRANSLATE_MARK("ANSI green color"), + B_TRANSLATE_MARK("ANSI yellow color"), + B_TRANSLATE_MARK("ANSI blue color"), + B_TRANSLATE_MARK("ANSI magenta color"), + B_TRANSLATE_MARK("ANSI cyan color"), + B_TRANSLATE_MARK("ANSI white color"), + B_TRANSLATE_MARK("ANSI bright black color"), + B_TRANSLATE_MARK("ANSI bright red color"), + B_TRANSLATE_MARK("ANSI bright green color"), + B_TRANSLATE_MARK("ANSI bright yellow color"), + B_TRANSLATE_MARK("ANSI bright blue color"), + B_TRANSLATE_MARK("ANSI bright magenta color"), + B_TRANSLATE_MARK("ANSI bright cyan color"), + B_TRANSLATE_MARK("ANSI bright white color"), + NULL +}; + + +ThemeView::ThemeView(const char* name, const BMessenger& messenger) + : + BGroupView(name, B_VERTICAL, 5), + fTerminalMessenger(messenger) +{ + SetViewUIColor(B_PANEL_BACKGROUND_COLOR); + + // Set up list of color attributes + fAttrList = new ColorListView("AttributeList"); + + fScrollView = new BScrollView("ScrollView", fAttrList, 0, false, true); + fScrollView->SetViewUIColor(B_PANEL_BACKGROUND_COLOR); + + PrefHandler* prefHandler = PrefHandler::Default(); + + for (const char** table = kColorTable; *table != NULL; ++table) { + fAttrList->AddItem(new ColorItem(B_TRANSLATE_NOCOLLECT(*table), + prefHandler->getRGB(*table))); + } + + fColorSchemeMenu = new BPopUpMenu(""); + fColorSchemeField = new BMenuField(B_TRANSLATE("Color scheme:"), + fColorSchemeMenu); + + fColorPreview = new ColorPreview(new BMessage(COLOR_DROPPED), 0); + fColorPreview->SetExplicitAlignment(BAlignment(B_ALIGN_HORIZONTAL_CENTER, + B_ALIGN_VERTICAL_CENTER)); + + fPicker = new BColorControl(B_ORIGIN, B_CELLS_32x8, 8.0, + "picker", new BMessage(MSG_UPDATE_COLOR)); + + fPreview = new BTextView("preview"); + + BLayoutBuilder::Group<>(this) + .AddGrid() + .Add(fColorSchemeField->CreateLabelLayoutItem(), 0, 5) + .Add(fColorSchemeField->CreateMenuBarLayoutItem(), 1, 5) + .End() + .Add(fScrollView, 10.0) + .Add(fPreview) + .AddGroup(B_HORIZONTAL, B_USE_DEFAULT_SPACING) + .Add(fColorPreview) + .AddGlue() + .Add(fPicker); + + fColorPreview->Parent()->SetExplicitMaxSize( + BSize(B_SIZE_UNSET, fPicker->Bounds().Height())); + fAttrList->SetSelectionMessage(new BMessage(MSG_COLOR_ATTRIBUTE_CHOSEN)); + fScrollView->SetExplicitMinSize(BSize(B_SIZE_UNSET, 22 * 16)); + fColorSchemeField->SetAlignment(B_ALIGN_RIGHT); + + _MakeColorSchemeMenu(); + + _UpdateStyle(); + + InvalidateLayout(true); +} + + +ThemeView::~ThemeView() +{ +} + + +void +ThemeView::_UpdateStyle() +{ + PrefHandler* prefHandler = PrefHandler::Default(); + + const char* colours[] = { + B_TRANSLATE("black"), + B_TRANSLATE("red"), + B_TRANSLATE("green"), + B_TRANSLATE("yellow"), + B_TRANSLATE("blue"), + B_TRANSLATE("magenta"), + B_TRANSLATE("cyan"), + B_TRANSLATE("white"), + }; + + text_run_array *array = (text_run_array*)malloc(sizeof(text_run_array) + + sizeof(text_run) * 16); + int offset = 1; + int index = 0; + array->count = 16; + array->runs[0].offset = offset; + array->runs[0].font = *be_fixed_font; + array->runs[0].color = prefHandler->getRGB(PREF_ANSI_BLACK_COLOR); + offset += strlen(colours[index++]) + 1; + array->runs[1].offset = offset; + array->runs[1].font = *be_fixed_font; + array->runs[1].color = prefHandler->getRGB(PREF_ANSI_RED_COLOR); + offset += strlen(colours[index++]) + 1; + array->runs[2].offset = offset; + array->runs[2].font = *be_fixed_font; + array->runs[2].color = prefHandler->getRGB(PREF_ANSI_GREEN_COLOR); + offset += strlen(colours[index++]) + 1; + array->runs[3].offset = offset; + array->runs[3].font = *be_fixed_font; + array->runs[3].color = prefHandler->getRGB(PREF_ANSI_YELLOW_COLOR); + offset += strlen(colours[index++]) + 1; + array->runs[4].offset = offset; + array->runs[4].font = *be_fixed_font; + array->runs[4].color = prefHandler->getRGB(PREF_ANSI_BLUE_COLOR); + offset += strlen(colours[index++]) + 1; + array->runs[5].offset = offset; + array->runs[5].font = *be_fixed_font; + array->runs[5].color = prefHandler->getRGB(PREF_ANSI_MAGENTA_COLOR); + offset += strlen(colours[index++]) + 1; + array->runs[6].offset = offset; + array->runs[6].font = *be_fixed_font; + array->runs[6].color = prefHandler->getRGB(PREF_ANSI_CYAN_COLOR); + offset += strlen(colours[index++]) + 1; + array->runs[7].offset = offset; + array->runs[7].font = *be_fixed_font; + array->runs[7].color = prefHandler->getRGB(PREF_ANSI_WHITE_COLOR); + offset += strlen(colours[index++]) + 1; + index = 0; + offset++; + array->runs[8].offset = offset; + array->runs[8].font = *be_fixed_font; + array->runs[8].color = prefHandler->getRGB(PREF_ANSI_BLACK_HCOLOR); + offset += strlen(colours[index++]) + 1; + array->runs[9].offset = offset; + array->runs[9].font = *be_fixed_font; + array->runs[9].color = prefHandler->getRGB(PREF_ANSI_RED_HCOLOR); + offset += strlen(colours[index++]) + 1; + array->runs[10].offset = offset; + array->runs[10].font = *be_fixed_font; + array->runs[10].color = prefHandler->getRGB(PREF_ANSI_GREEN_HCOLOR); + offset += strlen(colours[index++]) + 1; + array->runs[11].offset = offset; + array->runs[11].font = *be_fixed_font; + array->runs[11].color = prefHandler->getRGB(PREF_ANSI_YELLOW_HCOLOR); + offset += strlen(colours[index++]) + 1; + array->runs[12].offset = offset; + array->runs[12].font = *be_fixed_font; + array->runs[12].color = prefHandler->getRGB(PREF_ANSI_BLUE_HCOLOR); + offset += strlen(colours[index++]) + 1; + array->runs[13].offset = offset; + array->runs[13].font = *be_fixed_font; + array->runs[13].color = prefHandler->getRGB(PREF_ANSI_MAGENTA_HCOLOR); + offset += strlen(colours[index++]) + 1; + array->runs[14].offset = offset; + array->runs[14].font = *be_fixed_font; + array->runs[14].color = prefHandler->getRGB(PREF_ANSI_CYAN_HCOLOR); + offset += strlen(colours[index++]) + 1; + array->runs[15].offset = offset; + array->runs[15].font = *be_fixed_font; + array->runs[15].color = prefHandler->getRGB(PREF_ANSI_WHITE_HCOLOR); + + fPreview->SetStylable(true); + fPreview->MakeEditable(false); + fPreview->MakeSelectable(false); + fPreview->SetViewColor(prefHandler->getRGB(PREF_TEXT_BACK_COLOR)); + + BString previewText; + previewText << '\n'; + for (int ix = 0; ix < 8; ++ix) + { + previewText << ' ' << colours[ix]; + } + previewText << '\n'; + for (int ix = 0; ix < 8; ++ix) + { + previewText << ' ' << colours[ix]; + } + previewText << '\n'; + + fPreview->SetAlignment(B_ALIGN_CENTER); + fPreview->SetText(previewText.String(), array); + font_height height; + be_fixed_font->GetHeight(&height); + fPreview->SetExplicitMinSize(BSize(B_SIZE_UNSET, (height.ascent + height.descent) * 5)); +} + + +void +ThemeView::AttachedToWindow() +{ + fPicker->SetTarget(this); + fAttrList->SetTarget(this); + fColorPreview->SetTarget(this); + fColorSchemeField->Menu()->SetTargetForItems(this); + + fAttrList->Select(0); + SetViewUIColor(B_PANEL_BACKGROUND_COLOR); + + _SetCurrentColorScheme(); +} + + +void +ThemeView::WindowActivated(bool active) +{ + if (!active) + return; + + UpdateMenu(); +} + + +void +ThemeView::UpdateMenu() +{ + PrefHandler::Default()->LoadThemes(); + + _MakeColorSchemeMenu(); + _SetCurrentColorScheme(); + + fColorSchemeField->Menu()->SetTargetForItems(this); +} + + +void +ThemeView::MessageReceived(BMessage *msg) +{ + bool modified = false; + + switch (msg->what) { + case MSG_COLOR_SCHEME_CHANGED: + { + color_scheme* newScheme = NULL; + if (msg->FindPointer("color_scheme", + (void**)&newScheme) == B_OK) { + _ChangeColorScheme(newScheme); + modified = true; + } + break; + } + + case MSG_SET_COLOR: + { + rgb_color* color; + ssize_t size; + const char* name; + + if (msg->FindData(kRGBColor, B_RGB_COLOR_TYPE, + (const void**)&color, &size) == B_OK + && msg->FindString(kName, &name) == B_OK) { + _SetColor(name, *color); + modified = true; + } + break; + } + + case MSG_SET_CURRENT_COLOR: + { + rgb_color* color; + ssize_t size; + + if (msg->FindData(kRGBColor, B_RGB_COLOR_TYPE, + (const void**)&color, &size) == B_OK) { + _SetCurrentColor(*color); + modified = true; + } + break; + } + + case MSG_UPDATE_COLOR: + { + // Received from the color fPicker when its color changes + rgb_color color = fPicker->ValueAsColor(); + _SetCurrentColor(color); + modified = true; + + break; + } + + case MSG_COLOR_ATTRIBUTE_CHOSEN: + { + // Received when the user chooses a GUI fAttribute from the list + ColorItem* item = (ColorItem*) + fAttrList->ItemAt(fAttrList->CurrentSelection()); + if (item == NULL) + break; + + const char* label = item->Text(); + rgb_color color = PrefHandler::Default()->getRGB(label); + _SetCurrentColor(color); + break; + } + + case MSG_THEME_MODIFIED: + _SetCurrentColorScheme(); + BView::MessageReceived(msg); + return; + + default: + BView::MessageReceived(msg); + break; + } + + if (modified) { + _UpdateStyle(); + fTerminalMessenger.SendMessage(msg); + + BMessenger messenger(this); + messenger.SendMessage(MSG_THEME_MODIFIED); + } +} + + +void +ThemeView::SetDefaults() +{ + PrefHandler* prefHandler = PrefHandler::Default(); + + int32 count = fAttrList->CountItems(); + for (int32 index = 0; index < count; ++index) { + ColorItem* item = (ColorItem*)fAttrList->ItemAt(index); + item->SetColor(prefHandler->getRGB(item->Text())); + fAttrList->InvalidateItem(index); + } + + int32 currentIndex = fAttrList->CurrentSelection(); + ColorItem* item = (ColorItem*)fAttrList->ItemAt(currentIndex); + if (item != NULL) { + rgb_color color = item->Color(); + fPicker->SetValue(color); + fColorPreview->SetColor(color); + fColorPreview->Invalidate(); + } + + _UpdateStyle(); + + BMessage message(MSG_COLOR_SCHEME_CHANGED); + fTerminalMessenger.SendMessage(&message); +} + + +void +ThemeView::Revert() +{ + _SetCurrentColorScheme(); + + SetDefaults(); +} + + +void +ThemeView::_ChangeColorScheme(color_scheme* scheme) +{ + PrefHandler* pref = PrefHandler::Default(); + + pref->setRGB(PREF_TEXT_FORE_COLOR, scheme->text_fore_color); + pref->setRGB(PREF_TEXT_BACK_COLOR, scheme->text_back_color); + pref->setRGB(PREF_SELECT_FORE_COLOR, scheme->select_fore_color); + pref->setRGB(PREF_SELECT_BACK_COLOR, scheme->select_back_color); + pref->setRGB(PREF_CURSOR_FORE_COLOR, scheme->cursor_fore_color); + pref->setRGB(PREF_CURSOR_BACK_COLOR, scheme->cursor_back_color); + pref->setRGB(PREF_ANSI_BLACK_COLOR, scheme->ansi_colors.black); + pref->setRGB(PREF_ANSI_RED_COLOR, scheme->ansi_colors.red); + pref->setRGB(PREF_ANSI_GREEN_COLOR, scheme->ansi_colors.green); + pref->setRGB(PREF_ANSI_YELLOW_COLOR, scheme->ansi_colors.yellow); + pref->setRGB(PREF_ANSI_BLUE_COLOR, scheme->ansi_colors.blue); + pref->setRGB(PREF_ANSI_MAGENTA_COLOR, scheme->ansi_colors.magenta); + pref->setRGB(PREF_ANSI_CYAN_COLOR, scheme->ansi_colors.cyan); + pref->setRGB(PREF_ANSI_WHITE_COLOR, scheme->ansi_colors.white); + pref->setRGB(PREF_ANSI_BLACK_HCOLOR, scheme->ansi_colors_h.black); + pref->setRGB(PREF_ANSI_RED_HCOLOR, scheme->ansi_colors_h.red); + pref->setRGB(PREF_ANSI_GREEN_HCOLOR, scheme->ansi_colors_h.green); + pref->setRGB(PREF_ANSI_YELLOW_HCOLOR, scheme->ansi_colors_h.yellow); + pref->setRGB(PREF_ANSI_BLUE_HCOLOR, scheme->ansi_colors_h.blue); + pref->setRGB(PREF_ANSI_MAGENTA_HCOLOR, scheme->ansi_colors_h.magenta); + pref->setRGB(PREF_ANSI_CYAN_HCOLOR, scheme->ansi_colors_h.cyan); + pref->setRGB(PREF_ANSI_WHITE_HCOLOR, scheme->ansi_colors_h.white); + + int32 count = fAttrList->CountItems(); + for (int32 index = 0; index < count; ++index) + { + ColorItem* item = static_cast(fAttrList->ItemAt(index)); + rgb_color color = pref->getRGB(item->Text()); + item->SetColor(color); + + if (item->IsSelected()) { + fPicker->SetValue(color); + fColorPreview->SetColor(color); + fColorPreview->Invalidate(); + } + } + + fAttrList->Invalidate(); +} + + +void +ThemeView::_SetCurrentColorScheme() +{ + PrefHandler* pref = PrefHandler::Default(); + + pref->LoadColorScheme(&gCustomColorScheme); + + const char* currentSchemeName = NULL; + + int32 i = 0; + while (i < gColorSchemes->CountItems()) { + const color_scheme *item = gColorSchemes->ItemAt(i); + i++; + + if (gCustomColorScheme == *item) { + currentSchemeName = item->name; + break; + } + } + + // If the scheme is not one of the known ones, assume a custom one. + if (currentSchemeName == NULL) + currentSchemeName = "Custom"; + + for (int32 i = 0; i < fColorSchemeField->Menu()->CountItems(); i++) { + BMenuItem* item = fColorSchemeField->Menu()->ItemAt(i); + if (strcmp(item->Label(), currentSchemeName) == 0) { + item->SetMarked(true); + break; + } + } +} + + +void +ThemeView::_MakeColorSchemeMenuItem(const color_scheme *item) +{ + if (item == NULL) + return; + + BMessage* message = new BMessage(MSG_COLOR_SCHEME_CHANGED); + message->AddPointer("color_scheme", (const void*)item); + fColorSchemeMenu->AddItem(new BMenuItem(item->name, message)); +} + + +void +ThemeView::_MakeColorSchemeMenu() +{ + while (fColorSchemeMenu->CountItems() > 0) + delete(fColorSchemeMenu->RemoveItem((int32)0)); + + FindColorSchemeByName comparator("Default"); + + const color_scheme* defaultItem = gColorSchemes->FindIf(comparator); + + _MakeColorSchemeMenuItem(defaultItem); + + int32 index = 0; + while (index < gColorSchemes->CountItems()) { + const color_scheme *item = gColorSchemes->ItemAt(index); + index++; + + if (strcmp(item->name, "") == 0) + fColorSchemeMenu->AddSeparatorItem(); + else if (item == defaultItem) + continue; + else + _MakeColorSchemeMenuItem(item); + } + + // Add the custom item at the very end + fColorSchemeMenu->AddSeparatorItem(); + _MakeColorSchemeMenuItem(&gCustomColorScheme); +} + + +void +ThemeView::_SetCurrentColor(rgb_color color) +{ + int32 currentIndex = fAttrList->CurrentSelection(); + ColorItem* item = (ColorItem*)fAttrList->ItemAt(currentIndex); + if (item != NULL) { + item->SetColor(color); + fAttrList->InvalidateItem(currentIndex); + + PrefHandler::Default()->setRGB(item->Text(), color); + } + + fPicker->SetValue(color); + fColorPreview->SetColor(color); + fColorPreview->Invalidate(); +} + + +void +ThemeView::_SetColor(const char* name, rgb_color color) +{ + PrefHandler::Default()->setRGB(name, color); + + _UpdateStyle(); +} diff --git a/src/apps/terminal/ThemeView.h b/src/apps/terminal/ThemeView.h new file mode 100644 index 0000000000..e93f69ba58 --- /dev/null +++ b/src/apps/terminal/ThemeView.h @@ -0,0 +1,91 @@ +/* + * Copyright 2022, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef THEME_VIEW_H +#define THEME_VIEW_H + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "Colors.h" + + +static const uint32 MSG_COLOR_SCHEME_CHANGED = 'mccs'; +static const uint32 MSG_SET_CURRENT_COLOR = 'sccl'; +static const uint32 MSG_UPDATE_COLOR = 'upcl'; +static const uint32 MSG_COLOR_ATTRIBUTE_CHOSEN = 'atch'; +static const uint32 MSG_THEME_MODIFIED = 'tmdf'; +static const uint32 MSG_SET_COLOR = 'sclr'; + +static const char* const kRGBColor = "RGBColor"; +static const char* const kName = "name"; + +class ThemeWindow; +class ColorPreview; +class BMenu; +class BMenuField; +class BPopUpMenu; +class BTextView; + +class ThemeView : public BGroupView { +public: + ThemeView(const char *name, + const BMessenger &messenger); + virtual ~ThemeView(); + + virtual void AttachedToWindow(); + virtual void WindowActivated(bool active); + virtual void MessageReceived(BMessage *msg); + + void SetDefaults(); + void Revert(); + void UpdateMenu(); + +private: + void _UpdateStyle(); + void _ChangeColorScheme(color_scheme* scheme); + void _SetCurrentColorScheme(); + void _SetCurrentColor(rgb_color color); + void _SetColor(const char* name, rgb_color color); + + void _MakeColorSchemeMenu(); + void _MakeColorSchemeMenuItem(const color_scheme *item); + +private: + BColorControl* fPicker; + BListView* fAttrList; + const char* fName; + BScrollView* fScrollView; + ColorPreview* fColorPreview; + BMenuField* fColorSchemeField; + BPopUpMenu* fColorSchemeMenu; + BTextView* fPreview; + + BMessage fPrevColors; + BMessage fDefaultColors; + BMessage fCurrentColors; + + BMessenger fTerminalMessenger; + +public: + static const char* kColorTable[]; +}; + +#endif // THEME_VIEW_H diff --git a/src/apps/terminal/ThemeWindow.cpp b/src/apps/terminal/ThemeWindow.cpp new file mode 100644 index 0000000000..24feb919f4 --- /dev/null +++ b/src/apps/terminal/ThemeWindow.cpp @@ -0,0 +1,205 @@ +/* + * Copyright 2022, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + */ + + +#include "ThemeWindow.h" + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "PrefHandler.h" +#include "TermConst.h" +#include "ThemeView.h" + + +#undef B_TRANSLATION_CONTEXT +#define B_TRANSLATION_CONTEXT "Terminal ThemeWindow" + +// local messages +const uint32 MSG_DEFAULTS_PRESSED = 'defl'; +const uint32 MSG_SAVEAS_PRESSED = 'canl'; +const uint32 MSG_REVERT_PRESSED = 'revt'; + + +ThemeWindow::ThemeWindow(const BMessenger& messenger) + : + BWindow(BRect(0, 0, 0, 0), B_TRANSLATE_SYSTEM_NAME("Theme settings"), B_TITLED_WINDOW, + B_NOT_RESIZABLE | B_NOT_ZOOMABLE | B_AUTO_UPDATE_SIZE_LIMITS), + fPreviousPref(new PrefHandler(PrefHandler::Default())), + fSavePanel(NULL), + fDirty(false), + fTerminalMessenger(messenger) +{ + fDefaultsButton = new BButton("defaults", B_TRANSLATE("Defaults"), + new BMessage(MSG_DEFAULTS_PRESSED), B_WILL_DRAW); + + fRevertButton = new BButton("revert", B_TRANSLATE("Revert"), + new BMessage(MSG_REVERT_PRESSED), B_WILL_DRAW); + + fSaveAsFileButton = new BButton("savebutton", + B_TRANSLATE("Save to file" B_UTF8_ELLIPSIS), + new BMessage(MSG_SAVEAS_PRESSED), B_WILL_DRAW); + + fThemeView = new ThemeView(B_TRANSLATE("Theme"), fTerminalMessenger); + + BLayoutBuilder::Group<>(this, B_VERTICAL, 0) + .SetInsets(B_USE_WINDOW_SPACING) + .Add(fThemeView) + .AddGroup(B_HORIZONTAL) + .Add(fDefaultsButton) + .Add(fRevertButton) + .AddGlue() + .Add(fSaveAsFileButton) + .SetInsets(0, B_USE_DEFAULT_SPACING, 0, 0); + + fRevertButton->SetEnabled(fDirty); + + AddShortcut('Q', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED)); + AddShortcut('W', B_COMMAND_KEY, new BMessage(B_QUIT_REQUESTED)); + + CenterOnScreen(); + Show(); +} + + +void +ThemeWindow::Quit() +{ + fTerminalMessenger.SendMessage(MSG_THEME_CLOSED); + delete fPreviousPref; + delete fSavePanel; + BWindow::Quit(); +} + + +bool +ThemeWindow::QuitRequested() +{ + if (fDirty) + _Save(); + + return true; +} + + +void +ThemeWindow::_SaveAs() +{ + if (!fSavePanel) { + BMessenger messenger(this); + fSavePanel = new BFilePanel(B_SAVE_PANEL, &messenger); + } + + BPath path; + if (find_directory(B_USER_SETTINGS_DIRECTORY, &path) == B_OK) { + path.Append("Terminal/Themes"); + create_directory(path.Path(), 0755); + fSavePanel->SetPanelDirectory(path.Path()); + } + + fSavePanel->Show(); +} + + +void +ThemeWindow::_SaveRequested(BMessage *msg) +{ + entry_ref dirref; + const char* filename; + + msg->FindRef("directory", &dirref); + msg->FindString("name", &filename); + + BDirectory dir(&dirref); + BPath path(&dir, filename); + + PrefHandler *prefHandler = PrefHandler::Default(); + + BFile file(path.Path(), B_WRITE_ONLY | B_CREATE_FILE | B_ERASE_FILE); + char buffer[512]; + + for (const char** table = ThemeView::kColorTable; *table != NULL; ++table) { + int len = snprintf(buffer, sizeof(buffer), "\"%s\" , \"%s\"\n", + *table, prefHandler->getString(*table)); + file.Write(buffer, len); + } + + // Name the theme after the filename + int len = snprintf(buffer, sizeof(buffer), "\"%s\" , \"%s\"\n", + PREF_THEME_NAME, filename); + file.Write(buffer, len); + + fThemeView->UpdateMenu(); +} + + +void +ThemeWindow::_Save() +{ + delete fPreviousPref; + fPreviousPref = new PrefHandler(PrefHandler::Default()); + + PrefHandler::Default()->SaveDefaultAsText(); + fDirty = false; +} + + +void +ThemeWindow::_Revert() +{ + if (fDirty) { + PrefHandler::SetDefault(new PrefHandler(fPreviousPref)); + + fThemeView->Revert(); + + fDirty = false; + fRevertButton->SetEnabled(fDirty); + } +} + + +void +ThemeWindow::MessageReceived(BMessage *message) +{ + switch (message->what) { + + case MSG_SAVEAS_PRESSED: + _SaveAs(); + break; + + case MSG_REVERT_PRESSED: + _Revert(); + break; + + case MSG_DEFAULTS_PRESSED: + PrefHandler::SetDefault(new PrefHandler(false)); + fThemeView->SetDefaults(); + // fallthrough + + case MSG_THEME_MODIFIED: + fDirty = true; + fRevertButton->SetEnabled(fDirty); + break; + + case B_SAVE_REQUESTED: + _SaveRequested(message); + break; + + default: + BWindow::MessageReceived(message); + break; + } +} diff --git a/src/apps/terminal/ThemeWindow.h b/src/apps/terminal/ThemeWindow.h new file mode 100644 index 0000000000..439e99038b --- /dev/null +++ b/src/apps/terminal/ThemeWindow.h @@ -0,0 +1,50 @@ +/* + * Copyright 2022, Haiku. All rights reserved. + * Distributed under the terms of the MIT License. + */ +#ifndef THEME_WINDOW_H +#define THEME_WINDOW_H + + +#include +#include +#include +#include + +// local messages +const uint32 MSG_THEME_CLOSED = 'mstc'; + +class BFilePanel; +class PrefHandler; +class ThemeView; + + +class ThemeWindow : public BWindow { +public: + ThemeWindow(const BMessenger &messenger); + virtual ~ThemeWindow() {}; + + virtual void MessageReceived(BMessage *message); + virtual void Quit(); + virtual bool QuitRequested(); + +private: + void _Save(); + void _SaveAs(); + void _Revert(); + void _SaveRequested(BMessage *message); + + PrefHandler* fPreviousPref; + BFilePanel* fSavePanel; + + ThemeView* fThemeView; + BButton* fDefaultsButton; + BButton* fRevertButton; + BButton* fSaveAsFileButton; + + bool fDirty; + BMessenger fTerminalMessenger; +}; + + +#endif