Step 196: high contrast and colorblind modes

This commit is contained in:
Bill
2026-02-10 04:55:49 -07:00
parent 93f2d7d1e9
commit 09fd7b011e
13 changed files with 219 additions and 3 deletions

View File

@@ -174,13 +174,27 @@
float x = xStart;
float amp = 2.0f;
bool up = true;
int segment = 0;
bool dotted = (d.severity != 1 && d.severity != 2);
while (x < xEnd) {
if (dotted) {
drawList->AddCircleFilled(ImVec2(x, baseY), 1.3f, color);
x += 4.0f;
continue;
}
float x2 = std::min(x + 4.0f, xEnd);
float y1 = baseY + (up ? -amp : amp);
float y2 = baseY + (up ? amp : -amp);
drawList->AddLine(ImVec2(x, y1), ImVec2(x2, y2), color, 1.0f);
bool drawSegment = true;
if (d.severity == 2) {
drawSegment = (segment % 2 == 0);
}
if (drawSegment) {
drawList->AddLine(ImVec2(x, y1), ImVec2(x2, y2), color, 1.0f);
}
up = !up;
x = x2;
segment++;
}
}
}

View File

@@ -1,6 +1,57 @@
#pragma once
// Included inside CodeEditorWidget (rendering + folds).
public:
static int annotationShapeIndex(const std::string& label) {
size_t h = 0;
for (unsigned char c : label) h = h * 131 + c;
return (int)(h % 5);
}
static void drawAnnotationShape(ImDrawList* drawList,
const ImVec2& center,
float size,
ImU32 color,
int shape) {
switch (shape) {
case 0: { // circle
drawList->AddCircleFilled(center, size, color);
break;
}
case 1: { // triangle
ImVec2 top(center.x, center.y - size);
ImVec2 left(center.x - size, center.y + size);
ImVec2 right(center.x + size, center.y + size);
drawList->AddTriangleFilled(top, left, right, color);
break;
}
case 2: { // square
ImVec2 a(center.x - size, center.y - size);
ImVec2 b(center.x + size, center.y + size);
drawList->AddRectFilled(a, b, color, 0.0f);
break;
}
case 3: { // diamond
ImVec2 top(center.x, center.y - size);
ImVec2 right(center.x + size, center.y);
ImVec2 bottom(center.x, center.y + size);
ImVec2 left(center.x - size, center.y);
drawList->AddQuadFilled(top, right, bottom, left, color);
break;
}
default: { // star (outline)
ImVec2 pts[10];
for (int i = 0; i < 10; ++i) {
float angle = (float)(IM_PI * 0.5 + i * (IM_PI / 5.0));
float radius = (i % 2 == 0) ? size : size * 0.5f;
pts[i] = ImVec2(center.x + std::cos(angle) * radius,
center.y - std::sin(angle) * radius);
}
drawList->AddPolyline(pts, 10, color, true, 1.5f);
break;
}
}
}
CodeEditorResult render(const char* id,
std::string& text,
const std::vector<HighlightSpan>& spans,
@@ -370,7 +421,12 @@ public:
AnnotationMarker marker;
if (annotationAtLine(*options.annotations, ln, marker)) {
ImVec2 center(origin.x + 12.0f, y + lineHeight * 0.5f);
drawList->AddCircleFilled(center, 3.0f, marker.color);
if (options.useAnnotationShapes) {
int shape = annotationShapeIndex(marker.message);
drawAnnotationShape(drawList, center, 3.5f, marker.color, shape);
} else {
drawList->AddCircleFilled(center, 3.0f, marker.color);
}
ImVec2 a(center.x - 4.0f, center.y - 4.0f);
ImVec2 b(center.x + 4.0f, center.y + 4.0f);
bool hoverAnno = ImGui::IsMouseHoveringRect(a, b);

View File

@@ -17,6 +17,7 @@
#include <array>
#include <algorithm>
#include <cctype>
#include <cmath>
#include <cstring>
struct MultiCursor {
@@ -34,6 +35,7 @@ struct CodeEditorOptions {
bool showAnnotations = false;
bool showLineNumbers = true;
bool showCurrentLine = true;
bool useAnnotationShapes = true;
float lineHeightScale = 1.0f;
float letterSpacing = 0.0f;
float cursorBlinkRate = 2.0f;

View File

@@ -58,6 +58,8 @@ public:
void setShowLineNumbers(bool value) { showLineNumbers_ = value; }
bool getReduceMotion() const { return reduceMotion_; }
void setReduceMotion(bool value) { reduceMotion_ = value; }
bool getUseAnnotationShapes() const { return useAnnotationShapes_; }
void setUseAnnotationShapes(bool value) { useAnnotationShapes_ = value; }
bool getBlockVulnerableImports() const { return blockVulnerableImports_; }
void setBlockVulnerableImports(bool value) { blockVulnerableImports_ = value; }
const std::string& getLayoutPreset() const { return layoutPreset_; }
@@ -85,6 +87,7 @@ public:
showMinimap_ = j.value("showMinimap", showMinimap_);
showLineNumbers_ = j.value("showLineNumbers", showLineNumbers_);
reduceMotion_ = j.value("reduceMotion", reduceMotion_);
useAnnotationShapes_ = j.value("useAnnotationShapes", useAnnotationShapes_);
blockVulnerableImports_ = j.value("blockVulnerableImports", blockVulnerableImports_);
layoutPreset_ = j.value("layoutPreset", layoutPreset_);
keybindingProfile_ = j.value("keybindingProfile", keybindingProfile_);
@@ -124,6 +127,7 @@ public:
j["showMinimap"] = showMinimap_;
j["showLineNumbers"] = showLineNumbers_;
j["reduceMotion"] = reduceMotion_;
j["useAnnotationShapes"] = useAnnotationShapes_;
j["blockVulnerableImports"] = blockVulnerableImports_;
j["layoutPreset"] = layoutPreset_;
j["keybindingProfile"] = keybindingProfile_;
@@ -200,6 +204,7 @@ private:
bool showMinimap_ = false;
bool showLineNumbers_ = true;
bool reduceMotion_ = false;
bool useAnnotationShapes_ = true;
bool blockVulnerableImports_ = false;
std::string layoutPreset_ = "VSCode";
std::string keybindingProfile_ = "VSCode";

View File

@@ -47,6 +47,8 @@ struct ThemeDefinition {
std::string sourcePath;
ImVec2 panelPadding = ImVec2(8.0f, 6.0f);
ImVec2 panelSpacing = ImVec2(8.0f, 6.0f);
float windowBorderSize = 1.0f;
float frameBorderSize = 0.0f;
};
class ThemeEngine {
@@ -326,6 +328,12 @@ private:
out.panelSpacing.x = layout["panelSpacing"][0].get<float>();
out.panelSpacing.y = layout["panelSpacing"][1].get<float>();
}
if (layout.contains("windowBorderSize")) {
out.windowBorderSize = layout["windowBorderSize"].get<float>();
}
if (layout.contains("frameBorderSize")) {
out.frameBorderSize = layout["frameBorderSize"].get<float>();
}
}
return true;
}
@@ -339,6 +347,8 @@ private:
}
style.WindowPadding = theme.panelPadding;
style.ItemSpacing = theme.panelSpacing;
style.WindowBorderSize = theme.windowBorderSize;
style.FrameBorderSize = theme.frameBorderSize;
}
static bool isSyntaxColor(ThemeColor color) {

View File

@@ -624,6 +624,7 @@ static void renderBottomPanel(EditorState& state) {
leftOpts.showWhitespace = state.ui.showWhitespace;
leftOpts.showLineNumbers = state.ui.showLineNumbers;
leftOpts.showCurrentLine = false;
leftOpts.useAnnotationShapes = state.settings.getUseAnnotationShapes();
leftOpts.lineHeightScale = state.settings.getLineHeightScale();
leftOpts.letterSpacing = state.settings.getLetterSpacing();
leftOpts.highlightLines = &state.diff.beforeLines;
@@ -641,6 +642,7 @@ static void renderBottomPanel(EditorState& state) {
rightOpts.showWhitespace = state.ui.showWhitespace;
rightOpts.showLineNumbers = state.ui.showLineNumbers;
rightOpts.showCurrentLine = false;
rightOpts.useAnnotationShapes = state.settings.getUseAnnotationShapes();
rightOpts.lineHeightScale = state.settings.getLineHeightScale();
rightOpts.letterSpacing = state.settings.getLetterSpacing();
rightOpts.highlightLines = &state.diff.afterLines;

View File

@@ -216,6 +216,7 @@ static void renderEditorPanel(EditorState& state) {
opts.showMinimap = state.ui.showMinimap;
opts.showAnnotations = state.ui.showAnnotations;
opts.showLineNumbers = state.ui.showLineNumbers;
opts.useAnnotationShapes = state.settings.getUseAnnotationShapes();
opts.lineHeightScale = state.settings.getLineHeightScale();
opts.letterSpacing = state.settings.getLetterSpacing();
opts.cursorBlinkRate = state.settings.getCursorBlinkRate();

View File

@@ -349,6 +349,11 @@ static void renderSettingsPanel(EditorState& state) {
state.settings.setReduceMotion(reduceMotionSetting);
settingsChanged = true;
}
bool useAnnotationShapes = state.settings.getUseAnnotationShapes();
if (ImGui::Checkbox("Use Shapes for Annotations", &useAnnotationShapes)) {
state.settings.setUseAnnotationShapes(useAnnotationShapes);
settingsChanged = true;
}
bool blockVulnImports = state.settings.getBlockVulnerableImports();
if (ImGui::Checkbox("Block Vulnerable Imports", &blockVulnImports)) {
state.settings.setBlockVulnerableImports(blockVulnImports);