#include "terminal_renderer.h" #include namespace crossdesk::terminal { namespace { std::string Clip(const std::string &text, size_t width) { if (text.size() <= width) return text; return width > 3 ? text.substr(0, width - 3) + "..." : text.substr(0, width); } std::string Compact(const std::vector &choices, size_t width) { for (const auto &text : choices) if (text.size() <= width) return text; return {}; } std::string Label(const std::string &label) { std::string text = label; text.resize(5, ' '); return text + " | "; } std::string CompactRow(const std::string &label, const std::vector &choices, size_t width) { const auto prefix = width >= 12 ? Label(label) : std::string{}; return prefix + Compact(choices, width - prefix.size()); } std::string Columns(const std::string &label, const std::vector &cells, size_t width, size_t count) { std::string line = Label(label); const size_t content = width - line.size() - (count - 1) * 3; for (size_t i = 0; i < cells.size(); ++i) { const size_t start = 8 + i * content / count + i * 3; if (i) { line.resize(start - 3, ' '); line += " | "; } line += cells[i]; } return line; } } // namespace std::vector StatusLines(const StatusBar &bar, int columns) { const size_t width = std::clamp(columns, 1, 240); const size_t margin = width >= 20 ? 1 : 0; const size_t inner = width - 2 * margin; const std::string mode = bar.command_mode ? (bar.control ? (inner < 36 ? "LOCAL PAUSED" : "LOCAL / CONTROL PAUSED") : (inner < 36 ? "LOCAL READ" : "LOCAL / READ ONLY")) : bar.control ? "CONTROL" : "READ ONLY"; const auto progress = bar.error.empty() ? bar.progress : "Error: " + bar.error; auto left = mode + " | " + progress; auto metrics = bar.fps; if (!bar.network.empty()) metrics += (metrics.empty() ? "" : " | ") + bar.network; auto right = bar.view + (metrics.empty() ? "" : " | " + metrics); if (left.size() + right.size() + 2 > inner) right = metrics; if (left.size() + right.size() + 2 > inner && bar.error.empty()) left = mode + " | " + bar.compact_progress; if (left.size() + right.size() + 2 > inner) right = bar.fps; if (left.size() + right.size() + 2 > inner) right.clear(); std::string status = Clip(left, inner); if (!right.empty()) { status.resize(inner - right.size(), ' '); status += right; } std::string keys, view; if (inner >= 63) { const size_t count = inner >= 99 ? 4 : inner >= 77 ? 3 : 2; if (bar.command_mode) { keys = Columns("LOCAL", count >= 3 ? std::vector{" Return", " Toggle control", " Quit"} : std::vector{" Return", " Toggle; Quit"}, inner, count); } else if (bar.control) { keys = Columns("KEYS", {" Commands", "Input -> remote"}, inner, count); } else { keys = Columns("KEYS", count >= 3 ? std::vector{" Control", " Quit", " Commands"} : std::vector{" Control; Quit", " Commands"}, inner, count); } if (bar.control && !bar.command_mode) { view = Columns("THEN", {" Toggle control", " Quit"}, inner, count); } else { view = Columns( "VIEW", count == 4 ? std::vector{" Pan", "<+/-> Zoom", " Fit", " Detail"} : count == 3 ? std::vector{" Pan", "<+/-> Zoom", " Fit; Detail"} : std::vector{" Pan; <+/-> Zoom", " Fit; Detail"}, inner, count); } } else { keys = CompactRow( bar.command_mode ? "LOCAL" : "KEYS", bar.command_mode ? std::vector{" Return | Toggle | Quit", " Return | Quit", " Back Quit", " Quit", "", "q"} : bar.control ? std::vector{" Commands | Input -> remote", " Commands", " Menu", ""} : std::vector< std::string>{" Control | Quit | Commands", " Control | Quit", " Ctrl Quit", " Quit", "", "q"}, inner); view = CompactRow( bar.control && !bar.command_mode ? "THEN" : "VIEW", bar.control && !bar.command_mode ? std::vector{" Toggle control | Quit", " Toggle; Quit", " / ", "", "q"} : std::vector{" Pan | <+/-> Zoom | Fit | " " Detail", "<+/-> Zoom | Fit | Detail", " Fit | Detail", " Fit", "", "f"}, inner); } std::vector lines{status, keys, view}; for (auto &line : lines) { line.insert(0, margin, ' '); line.resize(width, ' '); } return lines; } } // namespace crossdesk::terminal