|
|
@@ -1,6 +1,7 @@
|
|
|
#include "lumacs/theme.hpp"
|
|
|
#include <ncurses.h>
|
|
|
#include <algorithm>
|
|
|
+#include <climits>
|
|
|
|
|
|
namespace lumacs {
|
|
|
|
|
|
@@ -33,7 +34,6 @@ int Color::to_ncurses_color() const {
|
|
|
}
|
|
|
|
|
|
// Fallback to closest basic color
|
|
|
- // This is a simple approximation - could be improved
|
|
|
int min_dist = INT_MAX;
|
|
|
int closest = COLOR_WHITE;
|
|
|
|
|
|
@@ -63,67 +63,159 @@ int Color::to_ncurses_color() const {
|
|
|
return closest;
|
|
|
}
|
|
|
|
|
|
-bool Color::operator<(const Color& other) const {
|
|
|
- if (r != other.r) return r < other.r;
|
|
|
- if (g != other.g) return g < other.g;
|
|
|
- return b < other.b;
|
|
|
+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 {
|
|
|
+ auto it = faces_.find(name);
|
|
|
+ if (it != faces_.end()) {
|
|
|
+ return it->second;
|
|
|
+ }
|
|
|
+ return std::nullopt;
|
|
|
}
|
|
|
|
|
|
void Theme::set_color(ThemeElement element, const Color& fg, const Color& bg) {
|
|
|
- colors_[element] = {fg, bg};
|
|
|
- // Invalidate cached color pair
|
|
|
- color_pairs_.erase(element);
|
|
|
+ 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 it = colors_.find(element);
|
|
|
- if (it != colors_.end()) {
|
|
|
- return it->second.first;
|
|
|
- }
|
|
|
- // Default foreground
|
|
|
+ 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 it = colors_.find(element);
|
|
|
- if (it != colors_.end()) {
|
|
|
- return it->second.second;
|
|
|
- }
|
|
|
- // Default background
|
|
|
+ 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 {
|
|
|
- auto it = color_pairs_.find(element);
|
|
|
- if (it != color_pairs_.end()) {
|
|
|
+ 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
|
|
|
+ return 0; // Fallback to default if out of pairs
|
|
|
}
|
|
|
|
|
|
- Color fg = get_fg_color(element);
|
|
|
- Color bg = get_bg_color(element);
|
|
|
+ // 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);
|
|
|
- color_pairs_[element] = pair_id;
|
|
|
+ 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() {
|
|
|
- // Pre-initialize all color pairs for this theme
|
|
|
- for (auto& [element, colors] : colors_) {
|
|
|
- get_color_pair(element);
|
|
|
+ // 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_) {
|
|
|
@@ -159,7 +251,6 @@ std::shared_ptr<Theme> ThemeManager::create_everforest_theme() {
|
|
|
// Everforest color palette
|
|
|
Color bg0(45, 49, 48); // #2d3139
|
|
|
Color bg1(52, 56, 56); // #343839
|
|
|
- Color bg2(67, 72, 71); // #434847
|
|
|
Color fg(211, 198, 170); // #d3c6aa
|
|
|
|
|
|
Color red(230, 126, 128); // #e67e80
|
|
|
@@ -171,7 +262,7 @@ std::shared_ptr<Theme> ThemeManager::create_everforest_theme() {
|
|
|
Color purple(208, 135, 162); // #d699b5
|
|
|
Color grey(146, 131, 116); // #928374
|
|
|
|
|
|
- // Text elements
|
|
|
+ // Text elements via set_color (which now sets faces)
|
|
|
theme->set_color(ThemeElement::Normal, fg, bg0);
|
|
|
theme->set_color(ThemeElement::Keyword, red, bg0);
|
|
|
theme->set_color(ThemeElement::String, green, bg0);
|
|
|
@@ -274,4 +365,4 @@ std::shared_ptr<Theme> ThemeManager::create_dracula_theme() {
|
|
|
return theme;
|
|
|
}
|
|
|
|
|
|
-} // namespace lumacs
|
|
|
+} // namespace lumacs
|