| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269 |
- #include "lumacs/theme.hpp"
- #include <ncurses.h>
- #include <algorithm>
- #include <climits>
- namespace lumacs {
- static std::map<Color, int> color_cache;
- static int next_color_id = 8; // Start after the basic 8 colors
- int Color::to_ncurses_color() const {
- // Handle default colors
- if (r == -1 && g == -1 && b == -1) {
- return -1; // Use default
- }
-
- // Check cache first
- auto it = color_cache.find(*this);
- if (it != color_cache.end()) {
- return it->second;
- }
-
- // Check if we can create new colors
- if (can_change_color() && next_color_id < COLOR_PAIRS) {
- int color_id = next_color_id++;
- // Scale RGB values to 0-1000 range for ncurses
- int nr = (r * 1000) / 255;
- int ng = (g * 1000) / 255;
- int nb = (b * 1000) / 255;
-
- init_color(color_id, nr, ng, nb);
- color_cache[*this] = color_id;
- return color_id;
- }
-
- // Fallback to closest basic color
- int min_dist = INT_MAX;
- int closest = COLOR_WHITE;
-
- struct BasicColor { int id; int r, g, b; };
- BasicColor basic_colors[] = {
- {COLOR_BLACK, 0, 0, 0},
- {COLOR_RED, 255, 0, 0},
- {COLOR_GREEN, 0, 255, 0},
- {COLOR_YELLOW, 255, 255, 0},
- {COLOR_BLUE, 0, 0, 255},
- {COLOR_MAGENTA, 255, 0, 255},
- {COLOR_CYAN, 0, 255, 255},
- {COLOR_WHITE, 255, 255, 255}
- };
-
- for (auto& basic : basic_colors) {
- int dr = r - basic.r;
- int dg = g - basic.g;
- int db = b - basic.b;
- int dist = dr*dr + dg*dg + db*db;
- if (dist < min_dist) {
- min_dist = dist;
- closest = basic.id;
- }
- }
-
- return closest;
- }
- std::string Theme::element_to_face_name(ThemeElement element) {
- switch (element) {
- case ThemeElement::Normal: return "default";
- case ThemeElement::Keyword: return "font-lock-keyword-face";
- case ThemeElement::String: return "font-lock-string-face";
- case ThemeElement::Comment: return "font-lock-comment-face";
- case ThemeElement::Function: return "font-lock-function-name-face";
- case ThemeElement::Type: return "font-lock-type-face";
- case ThemeElement::Number: return "font-lock-constant-face"; // Mapping number to constant for now
- case ThemeElement::Constant: return "font-lock-constant-face";
- case ThemeElement::Error: return "error";
- case ThemeElement::StatusLine: return "mode-line";
- case ThemeElement::StatusLineInactive: return "mode-line-inactive";
- case ThemeElement::MessageLine: return "minibuffer-prompt";
- case ThemeElement::LineNumber: return "line-number";
- case ThemeElement::Cursor: return "cursor";
- case ThemeElement::Selection: return "region";
- case ThemeElement::SearchMatch: return "isearch";
- case ThemeElement::SearchFail: return "isearch-fail";
- case ThemeElement::WindowBorder: return "window-divider";
- case ThemeElement::WindowBorderActive: return "window-divider-active";
- case ThemeElement::Background: return "default"; // Background usually part of default
- default: return "default";
- }
- }
- void Theme::set_face(const std::string& name, const FaceAttributes& attrs) {
- faces_[name] = attrs;
- // Invalidate cache
- face_pairs_.erase(name);
- }
- std::optional<FaceAttributes> Theme::get_face(const std::string& name) const {
- return get_face_recursive(name, 0);
- }
- std::optional<FaceAttributes> Theme::get_face_recursive(const std::string& name, int depth) const {
- // Prevent infinite recursion
- if (depth > 10) {
- return std::nullopt;
- }
- auto it = faces_.find(name);
- if (it == faces_.end()) {
- return std::nullopt;
- }
- FaceAttributes current = it->second;
- // Handle inheritance
- if (current.inherit) {
- auto parent = get_face_recursive(*current.inherit, depth + 1);
- if (parent) {
- // Start with parent attributes
- FaceAttributes final_attrs = *parent;
- // Merge current (child) attributes on top, overriding parent
- final_attrs.merge(current);
- return final_attrs;
- }
- }
- return current;
- }
- void Theme::set_color(ThemeElement element, const Color& fg, const Color& bg) {
- std::string face_name = element_to_face_name(element);
- FaceAttributes attrs;
- attrs.foreground = fg;
- attrs.background = bg;
-
- // Merge with existing if possible, or set new
- auto existing = get_face(face_name);
- if (existing) {
- FaceAttributes merged = *existing;
- merged.foreground = fg;
- merged.background = bg;
- set_face(face_name, merged);
- } else {
- set_face(face_name, attrs);
- }
- }
- Color Theme::get_fg_color(ThemeElement element) const {
- auto face = get_face(element_to_face_name(element));
- if (face && face->foreground) return *face->foreground;
- return Color(255, 255, 255);
- }
- Color Theme::get_bg_color(ThemeElement element) const {
- auto face = get_face(element_to_face_name(element));
- if (face && face->background) return *face->background;
- return Color(-1, -1, -1);
- }
- int Theme::get_color_pair(ThemeElement element) const {
- return get_face_color_pair(element_to_face_name(element));
- }
- int Theme::get_face_color_pair(const std::string& name) const {
- // Check cache
- auto it = face_pairs_.find(name);
- if (it != face_pairs_.end()) {
- return COLOR_PAIR(it->second);
- }
-
- // Get attributes
- auto face = get_face(name);
-
- // If face not found, try default
- if (!face && name != "default") {
- face = get_face("default");
- }
-
- if (!face) {
- return 0; // No attributes found
- }
-
- // Create new color pair
- int pair_id = next_pair_id_++;
- if (pair_id >= COLOR_PAIRS) {
- return 0; // Fallback to default if out of pairs
- }
-
- // Resolve colors (fallback to default if missing)
- Color fg = face->foreground.value_or(Color(255, 255, 255));
- Color bg = face->background.value_or(Color(-1, -1, -1));
-
- // Special case: if this is a face that should inherit from default, we might need logic here.
- // For now, just use what we have.
-
- int fg_color = fg.to_ncurses_color();
- int bg_color = bg.to_ncurses_color();
-
- init_pair(pair_id, fg_color, bg_color);
- face_pairs_[name] = pair_id;
-
- return COLOR_PAIR(pair_id);
- }
- int Theme::get_face_attributes_ncurses(const std::string& name) const {
- int pair = get_face_color_pair(name);
- int attrs = pair;
-
- auto face = get_face(name);
- if (!face && name != "default") {
- face = get_face("default");
- }
-
- if (face) {
- if (face->weight == FontWeight::Bold) attrs |= A_BOLD;
- // A_ITALIC might not be defined on all ncurses versions, use ifdef or ignore for now
- #ifdef A_ITALIC
- if (face->slant == FontSlant::Italic) attrs |= A_ITALIC;
- #endif
- if (face->underline.value_or(false)) attrs |= A_UNDERLINE;
- if (face->inverse.value_or(false)) attrs |= A_REVERSE;
- }
-
- return attrs;
- }
- void Theme::initialize_ncurses_colors() {
- // Initialize known standard faces
- std::vector<std::string> standard_faces = {
- "default", "font-lock-keyword-face", "font-lock-string-face", "font-lock-comment-face",
- "font-lock-function-name-face", "font-lock-type-face", "font-lock-constant-face",
- "error", "mode-line", "mode-line-inactive", "minibuffer-prompt", "line-number",
- "cursor", "region", "isearch", "isearch-fail", "window-divider"
- };
-
- for (const auto& name : standard_faces) {
- get_face_color_pair(name);
- }
- }
- // ... (ThemeManager implementation remains mostly same, but ensures we set faces correctly)
- void ThemeManager::register_theme(std::shared_ptr<Theme> theme) {
- themes_[theme->name()] = theme;
- if (!active_theme_) {
- active_theme_ = theme;
- }
- }
- void ThemeManager::set_active_theme(const std::string& name) {
- auto it = themes_.find(name);
- if (it != themes_.end()) {
- active_theme_ = it->second;
- // The theme is already initialized (if necessary) when created by Lua
- }
- }
- std::vector<std::string> ThemeManager::theme_names() const {
- std::vector<std::string> names;
- for (auto& [name, theme] : themes_) {
- names.push_back(name);
- }
- return names;
- }
- // create_default_themes() and individual create_X_theme() methods are now removed.
- // Themes will be loaded from Lua scripts.
- } // namespace lumacs
|