Step 519: add modifier-edge shortcut notation model

This commit is contained in:
Bill
2026-02-17 09:02:06 -07:00
parent cced1be19f
commit 47c0c36274
4 changed files with 236 additions and 0 deletions

View File

@@ -3550,4 +3550,13 @@ target_link_libraries(step518_test PRIVATE
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
add_executable(step519_test tests/step519_test.cpp)
target_include_directories(step519_test PRIVATE src)
target_link_libraries(step519_test PRIVATE
nlohmann_json::nlohmann_json
unofficial::tree-sitter::tree-sitter
tree_sitter_python tree_sitter_cpp tree_sitter_elisp
tree_sitter_javascript tree_sitter_typescript
tree_sitter_java tree_sitter_rust tree_sitter_go)
# Step 12: Dear ImGui shell scaffolding created (main.cpp exists but not built due to dependencies)

View File

@@ -0,0 +1,76 @@
#pragma once
// Step 519: Modifier-Edge Shortcut Notation
#include <string>
#include <vector>
#include <algorithm>
struct ModifierEdges {
bool left = false; // Ctrl
bool right = false; // Shift
bool top = false; // Super/Win
bool bottom = false; // Alt
};
struct ShortcutGlyph {
char key = '?';
ModifierEdges edges;
std::string fallbackText;
};
class ModifierEdgeNotation {
public:
static ModifierEdges fromModifiers(bool ctrl, bool shift,
bool superKey, bool alt) {
return {ctrl, shift, superKey, alt};
}
static ShortcutGlyph buildGlyph(char key,
bool ctrl,
bool shift,
bool superKey,
bool alt,
bool glyphEnabled) {
ShortcutGlyph g;
g.key = normalizeKey(key);
g.edges = fromModifiers(ctrl, shift, superKey, alt);
g.fallbackText = fallbackLabel(g);
if (!glyphEnabled) {
g.edges = {};
}
return g;
}
static int activeEdgeCount(const ModifierEdges& e) {
return (e.left ? 1 : 0) + (e.right ? 1 : 0) +
(e.top ? 1 : 0) + (e.bottom ? 1 : 0);
}
static bool compactEnough(const ShortcutGlyph& g) {
// Key plus edge markers should fit compact control regions.
return g.key != '?' && g.fallbackText.size() <= 20;
}
static std::string fallbackLabel(const ShortcutGlyph& g) {
std::vector<std::string> parts;
if (g.edges.left) parts.push_back("Ctrl");
if (g.edges.right) parts.push_back("Shift");
if (g.edges.top) parts.push_back("Super");
if (g.edges.bottom) parts.push_back("Alt");
std::string out;
for (size_t i = 0; i < parts.size(); ++i) {
if (i > 0) out += "+";
out += parts[i];
}
if (!out.empty()) out += "+";
out += std::string(1, g.key);
return out;
}
private:
static char normalizeKey(char key) {
if (key >= 'a' && key <= 'z') return static_cast<char>(key - 'a' + 'A');
if ((key >= 'A' && key <= 'Z') || (key >= '0' && key <= '9')) return key;
return '?';
}
};

View File

@@ -0,0 +1,116 @@
// Step 519: Modifier-Edge Shortcut Notation (12 tests)
#include "ModifierEdgeNotation.h"
#include <iostream>
static int passed = 0, failed = 0;
#define TEST(name) { std::cout << " " << #name << "... "; }
#define PASS() { std::cout << "PASS\n"; ++passed; }
#define FAIL(msg) { std::cout << "FAIL: " << msg << "\n"; ++failed; }
#define CHECK(cond, msg) if (!(cond)) { FAIL(msg); return; } else {}
void test_ctrl_maps_to_left_edge() {
TEST(ctrl_maps_to_left_edge);
auto e = ModifierEdgeNotation::fromModifiers(true, false, false, false);
CHECK(e.left && !e.right && !e.top && !e.bottom, "ctrl edge mapping invalid");
PASS();
}
void test_shift_maps_to_right_edge() {
TEST(shift_maps_to_right_edge);
auto e = ModifierEdgeNotation::fromModifiers(false, true, false, false);
CHECK(e.right, "shift should map to right edge");
PASS();
}
void test_super_maps_to_top_edge() {
TEST(super_maps_to_top_edge);
auto e = ModifierEdgeNotation::fromModifiers(false, false, true, false);
CHECK(e.top, "super should map to top edge");
PASS();
}
void test_alt_maps_to_bottom_edge() {
TEST(alt_maps_to_bottom_edge);
auto e = ModifierEdgeNotation::fromModifiers(false, false, false, true);
CHECK(e.bottom, "alt should map to bottom edge");
PASS();
}
void test_combined_modifiers_set_multiple_edges() {
TEST(combined_modifiers_set_multiple_edges);
auto e = ModifierEdgeNotation::fromModifiers(true, true, false, true);
CHECK(ModifierEdgeNotation::activeEdgeCount(e) == 3, "expected 3 active edges");
PASS();
}
void test_key_is_normalized_to_uppercase() {
TEST(key_is_normalized_to_uppercase);
auto g = ModifierEdgeNotation::buildGlyph('k', true, false, false, false, true);
CHECK(g.key == 'K', "key should normalize to uppercase");
PASS();
}
void test_invalid_key_becomes_placeholder() {
TEST(invalid_key_becomes_placeholder);
auto g = ModifierEdgeNotation::buildGlyph('%', true, false, false, false, true);
CHECK(g.key == '?', "invalid key should become placeholder");
PASS();
}
void test_fallback_label_contains_modifiers_and_key() {
TEST(fallback_label_contains_modifiers_and_key);
auto g = ModifierEdgeNotation::buildGlyph('P', true, true, false, false, true);
CHECK(g.fallbackText == "Ctrl+Shift+P", "fallback label mismatch");
PASS();
}
void test_glyph_disabled_uses_fallback_no_edges() {
TEST(glyph_disabled_uses_fallback_no_edges);
auto g = ModifierEdgeNotation::buildGlyph('P', true, true, false, false, false);
CHECK(ModifierEdgeNotation::activeEdgeCount(g.edges) == 0, "edges should be cleared");
CHECK(g.fallbackText == "Ctrl+Shift+P", "fallback should remain available");
PASS();
}
void test_compact_check_passes_for_normal_shortcut() {
TEST(compact_check_passes_for_normal_shortcut);
auto g = ModifierEdgeNotation::buildGlyph('A', true, false, false, false, true);
CHECK(ModifierEdgeNotation::compactEnough(g), "shortcut should be compact");
PASS();
}
void test_active_edge_count_zero_without_modifiers() {
TEST(active_edge_count_zero_without_modifiers);
auto e = ModifierEdgeNotation::fromModifiers(false, false, false, false);
CHECK(ModifierEdgeNotation::activeEdgeCount(e) == 0, "edge count should be zero");
PASS();
}
void test_super_alt_combo_fallback_order_is_stable() {
TEST(super_alt_combo_fallback_order_is_stable);
auto g = ModifierEdgeNotation::buildGlyph('X', false, false, true, true, true);
CHECK(g.fallbackText == "Super+Alt+X", "modifier ordering should be stable");
PASS();
}
int main() {
std::cout << "Step 519: Modifier-Edge Shortcut Notation\n";
test_ctrl_maps_to_left_edge(); // 1
test_shift_maps_to_right_edge(); // 2
test_super_maps_to_top_edge(); // 3
test_alt_maps_to_bottom_edge(); // 4
test_combined_modifiers_set_multiple_edges(); // 5
test_key_is_normalized_to_uppercase(); // 6
test_invalid_key_becomes_placeholder(); // 7
test_fallback_label_contains_modifiers_and_key(); // 8
test_glyph_disabled_uses_fallback_no_edges(); // 9
test_compact_check_passes_for_normal_shortcut(); // 10
test_active_edge_count_zero_without_modifiers(); // 11
test_super_alt_combo_fallback_order_is_stable(); // 12
std::cout << "\nResults: " << passed << "/" << (passed + failed) << " passed\n";
return failed == 0 ? 0 : 1;
}

View File

@@ -8351,3 +8351,38 @@ to reduce visual drift between dense and sparse UI regions.
- `editor/src/IconTypographyHarmonizer.h` within header-size limit (`60` <= `600`)
- `editor/tests/step518_test.cpp` within test-file size guidance (`125` lines)
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`
### Step 519: Modifier-Edge Shortcut Notation
**Status:** PASS (12/12 tests)
Implements compact modifier-edge shortcut notation with deterministic edge
mapping (`Ctrl`=left, `Shift`=right, `Super`=top, `Alt`=bottom), combined
modifier support, and stable text fallback labels for non-glyph environments.
**Files added:**
- `editor/src/ModifierEdgeNotation.h` - notation module:
- modifier-to-edge mapping model
- glyph construction with key normalization
- edge-activity counting and compactness checks
- fallback label generation with stable modifier ordering
- `editor/tests/step519_test.cpp` - 12 tests covering:
- per-modifier edge mapping
- combined-edge behavior
- key normalization and invalid-key handling
- fallback-label correctness and ordering
- glyph-disabled fallback behavior
- compactness and edge-count sanity cases
**Files modified:**
- `editor/CMakeLists.txt` - `step519_test` target
**Verification run:**
- `cmake -S editor -B editor/build-native` - PASS
- `cmake --build editor/build-native --target step519_test` - PASS
- `./editor/build-native/step519_test` - PASS (12/12)
- `./editor/build-native/step518_test` - PASS (12/12) regression coverage
**Architecture gate check:**
- `editor/src/ModifierEdgeNotation.h` within header-size limit (`76` <= `600`)
- `editor/tests/step519_test.cpp` within test-file size guidance (`116` lines)
- Header-only architecture and naming conventions remain aligned with `ARCHITECTURE.md`