This commit is contained in:
2026-06-17 13:29:00 +03:00
parent 1c9dfbc17d
commit d0e306118f
7 changed files with 295 additions and 59 deletions
+1
View File
@@ -29,6 +29,7 @@ set(SORTLAB_SOURCES
src/Array.cpp src/Array.cpp
src/Sorter.cpp src/Sorter.cpp
src/UI.cpp src/UI.cpp
src/Theme.cpp
src/OperationsHistory.cpp src/OperationsHistory.cpp
src/ProgressMap.cpp src/ProgressMap.cpp
src/SortHistory.cpp src/SortHistory.cpp
+1
View File
@@ -8,6 +8,7 @@
#include "OperationsHistory.hpp" #include "OperationsHistory.hpp"
#include "ProgressMap.hpp" #include "ProgressMap.hpp"
#include "SortHistory.hpp" #include "SortHistory.hpp"
#include "Theme.hpp"
class App { class App {
public: public:
+81
View File
@@ -0,0 +1,81 @@
#pragma once
#include <SFML/Graphics.hpp>
enum class ThemeType {
DARK,
LIGHT
};
struct ColorPalette {
// Основные цвета фона
sf::Color windowBackground;
sf::Color panelBackground;
sf::Color bottomPanelBackground;
sf::Color overlayBackground;
// Цвета текста
sf::Color textPrimary;
sf::Color textSecondary;
sf::Color textTertiary;
sf::Color textAccent;
// Цвета для состояний элементов массива
sf::Color stateNormalLow; // Нижняя часть градиента для обычных элементов
sf::Color stateNormalHigh; // Верхняя часть градиента для обычных элементов
sf::Color stateCompare; // Цвет для сравниваемых элементов
sf::Color stateSwap; // Цвет для переставляемых элементов
sf::Color stateSorted; // Цвет для отсортированных элементов
// Цвета UI элементов
sf::Color separator;
sf::Color histogramLow; // Нижняя часть градиента гистограммы
sf::Color histogramHigh; // Верхняя часть градиента гистограммы
// Цвета для ProgressMap
sf::Color progressMapNormal;
// Цвета для оверлеев
sf::Color overlayTitle;
sf::Color overlaySubtitle;
sf::Color overlaySection;
sf::Color overlayBody;
sf::Color overlayFooter;
// Цвета для истории
sf::Color historyTableHeader;
sf::Color historyRowEven;
sf::Color historyRowOdd;
sf::Color historyNewestHighlight;
sf::Color historyNewestText;
sf::Color historySpeedFastest;
sf::Color historySpeedSlow;
// Цвета статусов
sf::Color statusFinished;
sf::Color statusPlaying;
sf::Color statusPaused;
};
class Theme {
public:
static Theme& getInstance();
void setTheme(ThemeType type);
ThemeType getThemeType() const;
void toggleTheme();
const ColorPalette& getPalette() const;
private:
Theme();
Theme(const Theme&) = delete;
Theme& operator=(const Theme&) = delete;
void initDarkTheme();
void initLightTheme();
ThemeType currentTheme_;
ColorPalette darkPalette_;
ColorPalette lightPalette_;
ColorPalette* currentPalette_;
};
+20 -13
View File
@@ -148,6 +148,10 @@ void App::handleEvents() {
} }
break; break;
case sf::Keyboard::T:
Theme::getInstance().toggleTheme();
break;
default: default:
break; break;
} }
@@ -230,7 +234,8 @@ void App::update(float dt) {
} }
void App::render() { void App::render() {
window_.clear(sf::Color(10, 12, 20)); const ColorPalette& palette = Theme::getInstance().getPalette();
window_.clear(palette.windowBackground);
const float topUIHeight = 80.0f; const float topUIHeight = 80.0f;
const float bottomPanelHeight = 140.0f; const float bottomPanelHeight = 140.0f;
@@ -256,20 +261,20 @@ void App::render() {
useGradient = true; useGradient = true;
break; break;
case Array::State::COMPARE: case Array::State::COMPARE:
barColor = sf::Color(0, 220, 255); barColor = palette.stateCompare;
break; break;
case Array::State::SWAP: case Array::State::SWAP:
barColor = sf::Color(255, 120, 30); barColor = palette.stateSwap;
break; break;
case Array::State::SORTED: case Array::State::SORTED:
barColor = sf::Color(50, 220, 120); barColor = palette.stateSorted;
break; break;
} }
if (useGradient) { if (useGradient) {
float normalizedValue = array_.getValue(i) / maxVal; float normalizedValue = array_.getValue(i) / maxVal;
sf::Color bottomColor(60, 80, 140); sf::Color bottomColor = palette.stateNormalLow;
sf::Color topColor(160, 180, 255); sf::Color topColor = palette.stateNormalHigh;
sf::Color gradientTop( sf::Color gradientTop(
static_cast<sf::Uint8>(bottomColor.r + (topColor.r - bottomColor.r) * normalizedValue), static_cast<sf::Uint8>(bottomColor.r + (topColor.r - bottomColor.r) * normalizedValue),
@@ -299,7 +304,7 @@ void App::render() {
sf::RectangleShape separator(sf::Vector2f(window_.getSize().x, 1.0f)); sf::RectangleShape separator(sf::Vector2f(window_.getSize().x, 1.0f));
separator.setPosition(0.0f, mainAreaBottom); separator.setPosition(0.0f, mainAreaBottom);
separator.setFillColor(sf::Color(40, 45, 60)); separator.setFillColor(palette.separator);
window_.draw(separator); window_.draw(separator);
renderDeltaHistogram(); renderDeltaHistogram();
@@ -307,7 +312,7 @@ void App::render() {
sf::RectangleShape verticalSeparator(sf::Vector2f(1.0f, bottomPanelHeight)); sf::RectangleShape verticalSeparator(sf::Vector2f(1.0f, bottomPanelHeight));
verticalSeparator.setPosition(1066.0f, mainAreaBottom); verticalSeparator.setPosition(1066.0f, mainAreaBottom);
verticalSeparator.setFillColor(sf::Color(40, 45, 60)); verticalSeparator.setFillColor(palette.separator);
window_.draw(verticalSeparator); window_.draw(verticalSeparator);
ui_.draw(window_); ui_.draw(window_);
@@ -324,6 +329,8 @@ void App::render() {
} }
void App::renderDeltaHistogram() { void App::renderDeltaHistogram() {
const ColorPalette& palette = Theme::getInstance().getPalette();
const float startX = 0.0f; const float startX = 0.0f;
const float startY = 760.0f; const float startY = 760.0f;
const float width = 1066.0f; const float width = 1066.0f;
@@ -331,7 +338,7 @@ void App::renderDeltaHistogram() {
sf::RectangleShape background(sf::Vector2f(width, height)); sf::RectangleShape background(sf::Vector2f(width, height));
background.setPosition(startX, startY); background.setPosition(startX, startY);
background.setFillColor(sf::Color(12, 15, 25)); background.setFillColor(palette.bottomPanelBackground);
window_.draw(background); window_.draw(background);
const auto& deltaHistory = opsHistory_.getDeltaHistory(); const auto& deltaHistory = opsHistory_.getDeltaHistory();
@@ -350,8 +357,8 @@ void App::renderDeltaHistogram() {
float y = startY + height - barHeight; float y = startY + height - barHeight;
float normalizedValue = static_cast<float>(deltaHistory[i]) / static_cast<float>(maxDelta); float normalizedValue = static_cast<float>(deltaHistory[i]) / static_cast<float>(maxDelta);
sf::Color lowColor(0, 80, 200); sf::Color lowColor = palette.histogramLow;
sf::Color highColor(0, 240, 255); sf::Color highColor = palette.histogramHigh;
sf::Color barColor( sf::Color barColor(
static_cast<sf::Uint8>(lowColor.r + (highColor.r - lowColor.r) * normalizedValue), static_cast<sf::Uint8>(lowColor.r + (highColor.r - lowColor.r) * normalizedValue),
@@ -370,7 +377,7 @@ void App::renderDeltaHistogram() {
sf::RectangleShape baseline(sf::Vector2f(width, 1.0f)); sf::RectangleShape baseline(sf::Vector2f(width, 1.0f));
baseline.setPosition(startX, startY + height - 1.0f); baseline.setPosition(startX, startY + height - 1.0f);
baseline.setFillColor(sf::Color(60, 70, 90)); baseline.setFillColor(palette.separator);
window_.draw(baseline); window_.draw(baseline);
if (bottomPanelFontLoaded_) { if (bottomPanelFontLoaded_) {
@@ -378,7 +385,7 @@ void App::renderDeltaHistogram() {
label.setFont(bottomPanelFont_); label.setFont(bottomPanelFont_);
label.setString("Delta Comparisons / step"); label.setString("Delta Comparisons / step");
label.setCharacterSize(13); label.setCharacterSize(13);
label.setFillColor(sf::Color(180, 180, 180)); label.setFillColor(palette.textTertiary);
label.setPosition(startX + 10.0f, startY + 5.0f); label.setPosition(startX + 10.0f, startY + 5.0f);
window_.draw(label); window_.draw(label);
} }
+12 -7
View File
@@ -1,10 +1,13 @@
#include "ProgressMap.hpp" #include "ProgressMap.hpp"
#include "Theme.hpp"
#include <cmath> #include <cmath>
ProgressMap::ProgressMap() { ProgressMap::ProgressMap() {
} }
void ProgressMap::draw(sf::RenderWindow& window, const Array& array, sf::Font& font, bool fontLoaded) { void ProgressMap::draw(sf::RenderWindow& window, const Array& array, sf::Font& font, bool fontLoaded) {
const ColorPalette& palette = Theme::getInstance().getPalette();
const float startX = 1066.0f; const float startX = 1066.0f;
const float startY = 760.0f; const float startY = 760.0f;
const float width = 534.0f; const float width = 534.0f;
@@ -12,7 +15,7 @@ void ProgressMap::draw(sf::RenderWindow& window, const Array& array, sf::Font& f
sf::RectangleShape background(sf::Vector2f(width, height)); sf::RectangleShape background(sf::Vector2f(width, height));
background.setPosition(startX, startY); background.setPosition(startX, startY);
background.setFillColor(sf::Color(12, 15, 25)); background.setFillColor(palette.bottomPanelBackground);
window.draw(background); window.draw(background);
if (fontLoaded) { if (fontLoaded) {
@@ -20,7 +23,7 @@ void ProgressMap::draw(sf::RenderWindow& window, const Array& array, sf::Font& f
label.setFont(font); label.setFont(font);
label.setString("Progress Map"); label.setString("Progress Map");
label.setCharacterSize(13); label.setCharacterSize(13);
label.setFillColor(sf::Color(180, 180, 180)); label.setFillColor(palette.textTertiary);
label.setPosition(startX + 10.0f, startY + 5.0f); label.setPosition(startX + 10.0f, startY + 5.0f);
window.draw(label); window.draw(label);
} }
@@ -67,16 +70,18 @@ void ProgressMap::draw(sf::RenderWindow& window, const Array& array, sf::Font& f
} }
sf::Color ProgressMap::getColorForState(Array::State state) const { sf::Color ProgressMap::getColorForState(Array::State state) const {
const ColorPalette& palette = Theme::getInstance().getPalette();
switch (state) { switch (state) {
case Array::State::NORMAL: case Array::State::NORMAL:
return sf::Color(100, 120, 150); return palette.progressMapNormal;
case Array::State::COMPARE: case Array::State::COMPARE:
return sf::Color(0, 220, 255); return palette.stateCompare;
case Array::State::SWAP: case Array::State::SWAP:
return sf::Color(255, 120, 30); return palette.stateSwap;
case Array::State::SORTED: case Array::State::SORTED:
return sf::Color(50, 220, 120); return palette.stateSorted;
default: default:
return sf::Color(100, 120, 150); return palette.progressMapNormal;
} }
} }
+119
View File
@@ -0,0 +1,119 @@
#include "Theme.hpp"
Theme& Theme::getInstance() {
static Theme instance;
return instance;
}
Theme::Theme() : currentTheme_(ThemeType::DARK) {
initDarkTheme();
initLightTheme();
currentPalette_ = &darkPalette_;
}
void Theme::initDarkTheme() {
// Основные цвета фона
darkPalette_.windowBackground = sf::Color(10, 12, 20);
darkPalette_.panelBackground = sf::Color(0, 0, 0, 180);
darkPalette_.bottomPanelBackground = sf::Color(12, 15, 25);
darkPalette_.overlayBackground = sf::Color(8, 10, 18, 230);
// Цвета текста
darkPalette_.textPrimary = sf::Color::White;
darkPalette_.textSecondary = sf::Color(220, 220, 220);
darkPalette_.textTertiary = sf::Color(160, 160, 160);
darkPalette_.textAccent = sf::Color(0, 220, 255);
darkPalette_.stateNormalLow = sf::Color(60, 80, 140);
darkPalette_.stateNormalHigh = sf::Color(160, 180, 255);
darkPalette_.stateCompare = sf::Color(0, 220, 255);
darkPalette_.stateSwap = sf::Color(255, 120, 30);
darkPalette_.stateSorted = sf::Color(50, 220, 120);
darkPalette_.separator = sf::Color(40, 45, 60);
darkPalette_.histogramLow = sf::Color(0, 80, 200);
darkPalette_.histogramHigh = sf::Color(0, 240, 255);
darkPalette_.progressMapNormal = sf::Color(100, 120, 150);
darkPalette_.overlayTitle = sf::Color::White;
darkPalette_.overlaySubtitle = sf::Color(160, 160, 160);
darkPalette_.overlaySection = sf::Color(0, 220, 255);
darkPalette_.overlayBody = sf::Color(200, 200, 200);
darkPalette_.overlayFooter = sf::Color(100, 100, 100);
darkPalette_.historyTableHeader = sf::Color(0, 220, 255);
darkPalette_.historyRowEven = sf::Color(0, 0, 0, 0);
darkPalette_.historyRowOdd = sf::Color(15, 18, 25, 100);
darkPalette_.historyNewestHighlight = sf::Color(30, 60, 90, 150);
darkPalette_.historyNewestText = sf::Color(100, 255, 150);
darkPalette_.historySpeedFastest = sf::Color(100, 255, 120);
darkPalette_.historySpeedSlow = sf::Color(255, 160, 60);
darkPalette_.statusFinished = sf::Color::Green;
darkPalette_.statusPlaying = sf::Color::Yellow;
darkPalette_.statusPaused = sf::Color::White;
}
void Theme::initLightTheme() {
lightPalette_.windowBackground = sf::Color(245, 247, 250);
lightPalette_.panelBackground = sf::Color(255, 255, 255, 200);
lightPalette_.bottomPanelBackground = sf::Color(235, 238, 245);
lightPalette_.overlayBackground = sf::Color(250, 252, 255, 240);
lightPalette_.textPrimary = sf::Color(20, 20, 30);
lightPalette_.textSecondary = sf::Color(40, 40, 50);
lightPalette_.textTertiary = sf::Color(100, 100, 120);
lightPalette_.textAccent = sf::Color(0, 120, 200);
lightPalette_.stateNormalLow = sf::Color(140, 160, 200);
lightPalette_.stateNormalHigh = sf::Color(80, 120, 200);
lightPalette_.stateCompare = sf::Color(0, 180, 255);
lightPalette_.stateSwap = sf::Color(255, 100, 20);
lightPalette_.stateSorted = sf::Color(40, 180, 80);
lightPalette_.separator = sf::Color(200, 205, 215);
lightPalette_.histogramLow = sf::Color(100, 150, 255);
lightPalette_.histogramHigh = sf::Color(0, 100, 200);
lightPalette_.progressMapNormal = sf::Color(160, 170, 190);
lightPalette_.overlayTitle = sf::Color(20, 20, 30);
lightPalette_.overlaySubtitle = sf::Color(100, 100, 120);
lightPalette_.overlaySection = sf::Color(0, 120, 200);
lightPalette_.overlayBody = sf::Color(40, 40, 50);
lightPalette_.overlayFooter = sf::Color(140, 140, 150);
lightPalette_.historyTableHeader = sf::Color(0, 120, 200);
lightPalette_.historyRowEven = sf::Color(0, 0, 0, 0);
lightPalette_.historyRowOdd = sf::Color(230, 235, 245, 150);
lightPalette_.historyNewestHighlight = sf::Color(200, 230, 255, 180);
lightPalette_.historyNewestText = sf::Color(0, 150, 80);
lightPalette_.historySpeedFastest = sf::Color(40, 180, 80);
lightPalette_.historySpeedSlow = sf::Color(255, 130, 40);
lightPalette_.statusFinished = sf::Color(0, 150, 60);
lightPalette_.statusPlaying = sf::Color(200, 150, 0);
lightPalette_.statusPaused = sf::Color(60, 60, 80);
}
void Theme::setTheme(ThemeType type) {
currentTheme_ = type;
currentPalette_ = (type == ThemeType::DARK) ? &darkPalette_ : &lightPalette_;
}
ThemeType Theme::getThemeType() const {
return currentTheme_;
}
void Theme::toggleTheme() {
if (currentTheme_ == ThemeType::DARK) {
setTheme(ThemeType::LIGHT);
} else {
setTheme(ThemeType::DARK);
}
}
const ColorPalette& Theme::getPalette() const {
return *currentPalette_;
}
+61 -39
View File
@@ -1,5 +1,6 @@
#pragma execution_character_set("utf-8") #pragma execution_character_set("utf-8")
#include "UI.hpp" #include "UI.hpp"
#include "Theme.hpp"
#include <iostream> #include <iostream>
#include <cmath> #include <cmath>
@@ -12,56 +13,58 @@ UI::UI() : fontLoaded_(false), historyOpen_(false) {
font_.setSmooth(true); font_.setSmooth(true);
fontLoaded_ = true; fontLoaded_ = true;
const ColorPalette& palette = Theme::getInstance().getPalette();
algorithmText_.setFont(font_); algorithmText_.setFont(font_);
algorithmText_.setCharacterSize(56); algorithmText_.setCharacterSize(56);
algorithmText_.setScale(0.5f, 0.5f); algorithmText_.setScale(0.5f, 0.5f);
algorithmText_.setFillColor(sf::Color::White); algorithmText_.setFillColor(palette.textPrimary);
algorithmText_.setPosition(15.0f, 15.0f); algorithmText_.setPosition(15.0f, 15.0f);
stateText_.setFont(font_); stateText_.setFont(font_);
stateText_.setCharacterSize(40); stateText_.setCharacterSize(40);
stateText_.setScale(0.5f, 0.5f); stateText_.setScale(0.5f, 0.5f);
stateText_.setFillColor(sf::Color::White); stateText_.setFillColor(palette.textPrimary);
stateText_.setPosition(15.0f, 50.0f); stateText_.setPosition(15.0f, 50.0f);
timeComplexityText_.setFont(font_); timeComplexityText_.setFont(font_);
timeComplexityText_.setCharacterSize(32); timeComplexityText_.setCharacterSize(32);
timeComplexityText_.setScale(0.5f, 0.5f); timeComplexityText_.setScale(0.5f, 0.5f);
timeComplexityText_.setFillColor(sf::Color(160, 160, 160)); timeComplexityText_.setFillColor(palette.textTertiary);
timeComplexityText_.setPosition(15.0f, 80.0f); timeComplexityText_.setPosition(15.0f, 80.0f);
spaceComplexityText_.setFont(font_); spaceComplexityText_.setFont(font_);
spaceComplexityText_.setCharacterSize(32); spaceComplexityText_.setCharacterSize(32);
spaceComplexityText_.setScale(0.5f, 0.5f); spaceComplexityText_.setScale(0.5f, 0.5f);
spaceComplexityText_.setFillColor(sf::Color(160, 160, 160)); spaceComplexityText_.setFillColor(palette.textTertiary);
spaceComplexityText_.setPosition(15.0f, 105.0f); spaceComplexityText_.setPosition(15.0f, 105.0f);
comparisonsText_.setFont(font_); comparisonsText_.setFont(font_);
comparisonsText_.setCharacterSize(36); comparisonsText_.setCharacterSize(36);
comparisonsText_.setScale(0.5f, 0.5f); comparisonsText_.setScale(0.5f, 0.5f);
comparisonsText_.setFillColor(sf::Color(220, 220, 220)); comparisonsText_.setFillColor(palette.textSecondary);
comparisonsText_.setPosition(15.0f, 135.0f); comparisonsText_.setPosition(15.0f, 135.0f);
swapsText_.setFont(font_); swapsText_.setFont(font_);
swapsText_.setCharacterSize(36); swapsText_.setCharacterSize(36);
swapsText_.setScale(0.5f, 0.5f); swapsText_.setScale(0.5f, 0.5f);
swapsText_.setFillColor(sf::Color(220, 220, 220)); swapsText_.setFillColor(palette.textSecondary);
swapsText_.setPosition(15.0f, 160.0f); swapsText_.setPosition(15.0f, 160.0f);
speedText_.setFont(font_); speedText_.setFont(font_);
speedText_.setCharacterSize(36); speedText_.setCharacterSize(36);
speedText_.setScale(0.5f, 0.5f); speedText_.setScale(0.5f, 0.5f);
speedText_.setFillColor(sf::Color(220, 220, 220)); speedText_.setFillColor(palette.textSecondary);
speedText_.setPosition(15.0f, 185.0f); speedText_.setPosition(15.0f, 185.0f);
controlsText_.setFont(font_); controlsText_.setFont(font_);
controlsText_.setCharacterSize(30); controlsText_.setCharacterSize(30);
controlsText_.setScale(0.5f, 0.5f); controlsText_.setScale(0.5f, 0.5f);
controlsText_.setFillColor(sf::Color(200, 200, 200)); controlsText_.setFillColor(palette.textSecondary);
controlsText_.setString("[1-8] Algorithms [Space] Play/Pause [Right] Step [Up/Down] Speed [R] Shuffle"); controlsText_.setString("[1-8] Algorithms [Space] Play/Pause [Right] Step [Up/Down] Speed [R] Shuffle [T] Theme");
leftBackground_.setFillColor(sf::Color(0, 0, 0, 180)); leftBackground_.setFillColor(palette.panelBackground);
rightBackground_.setFillColor(sf::Color(0, 0, 0, 180)); rightBackground_.setFillColor(palette.panelBackground);
} }
void UI::update(const Sorter& sorter, bool isPlaying, bool isFinished, int stepsPerFrame, const Array& array) { void UI::update(const Sorter& sorter, bool isPlaying, bool isFinished, int stepsPerFrame, const Array& array) {
@@ -69,18 +72,20 @@ void UI::update(const Sorter& sorter, bool isPlaying, bool isFinished, int steps
return; return;
} }
const ColorPalette& palette = Theme::getInstance().getPalette();
algorithmText_.setString("Algorithm: " + sorter.getName()); algorithmText_.setString("Algorithm: " + sorter.getName());
std::string state; std::string state;
if (isFinished) { if (isFinished) {
state = "Status: Finished"; state = "Status: Finished";
stateText_.setFillColor(sf::Color::Green); stateText_.setFillColor(palette.statusFinished);
} else if (isPlaying) { } else if (isPlaying) {
state = "Status: Playing"; state = "Status: Playing";
stateText_.setFillColor(sf::Color::Yellow); stateText_.setFillColor(palette.statusPlaying);
} else { } else {
state = "Status: Paused"; state = "Status: Paused";
stateText_.setFillColor(sf::Color::White); stateText_.setFillColor(palette.statusPaused);
} }
stateText_.setString(state); stateText_.setString(state);
@@ -97,6 +102,19 @@ void UI::draw(sf::RenderWindow& window) {
return; return;
} }
const ColorPalette& palette = Theme::getInstance().getPalette();
// Обновляем цвета текста и фона при каждой отрисовке (на случай смены темы)
algorithmText_.setFillColor(palette.textPrimary);
timeComplexityText_.setFillColor(palette.textTertiary);
spaceComplexityText_.setFillColor(palette.textTertiary);
comparisonsText_.setFillColor(palette.textSecondary);
swapsText_.setFillColor(palette.textSecondary);
speedText_.setFillColor(palette.textSecondary);
controlsText_.setFillColor(palette.textSecondary);
leftBackground_.setFillColor(palette.panelBackground);
rightBackground_.setFillColor(palette.panelBackground);
float maxWidth = 0.0f; float maxWidth = 0.0f;
sf::FloatRect algoBounds = algorithmText_.getGlobalBounds(); sf::FloatRect algoBounds = algorithmText_.getGlobalBounds();
@@ -148,6 +166,8 @@ void UI::drawInfoOverlay(sf::RenderWindow& window) {
return; return;
} }
const ColorPalette& palette = Theme::getInstance().getPalette();
auto toSfStr = [](const char* utf8) -> sf::String { auto toSfStr = [](const char* utf8) -> sf::String {
std::string s(utf8); std::string s(utf8);
return sf::String::fromUtf8(s.begin(), s.end()); return sf::String::fromUtf8(s.begin(), s.end());
@@ -158,85 +178,85 @@ void UI::drawInfoOverlay(sf::RenderWindow& window) {
sf::RectangleShape overlay(sf::Vector2f(windowWidth, windowHeight)); sf::RectangleShape overlay(sf::Vector2f(windowWidth, windowHeight));
overlay.setPosition(0.0f, 0.0f); overlay.setPosition(0.0f, 0.0f);
overlay.setFillColor(sf::Color(8, 10, 18, 230)); overlay.setFillColor(palette.overlayBackground);
window.draw(overlay); window.draw(overlay);
sf::Text title; sf::Text title;
title.setFont(font_); title.setFont(font_);
title.setCharacterSize(80); title.setCharacterSize(80);
title.setScale(0.5f, 0.5f); title.setScale(0.5f, 0.5f);
title.setFillColor(sf::Color::White); title.setFillColor(palette.overlayTitle);
title.setString(toSfStr(u8"SortLab — визуализатор алгоритмов сортировки")); title.setString(toSfStr(u8"SortLab — визуализатор алгоритмов сортировки"));
sf::Text subtitle; sf::Text subtitle;
subtitle.setFont(font_); subtitle.setFont(font_);
subtitle.setCharacterSize(38); subtitle.setCharacterSize(38);
subtitle.setScale(0.5f, 0.5f); subtitle.setScale(0.5f, 0.5f);
subtitle.setFillColor(sf::Color(160, 160, 160)); subtitle.setFillColor(palette.overlaySubtitle);
subtitle.setString(toSfStr(u8"Учебный инструмент для наглядного изучения алгоритмов сортировки")); subtitle.setString(toSfStr(u8"Учебный инструмент для наглядного изучения алгоритмов сортировки"));
sf::Text section1; sf::Text section1;
section1.setFont(font_); section1.setFont(font_);
section1.setCharacterSize(42); section1.setCharacterSize(42);
section1.setScale(0.5f, 0.5f); section1.setScale(0.5f, 0.5f);
section1.setFillColor(sf::Color(0, 220, 255)); section1.setFillColor(palette.overlaySection);
section1.setString(toSfStr(u8"О программе")); section1.setString(toSfStr(u8"О программе"));
sf::Text body1; sf::Text body1;
body1.setFont(font_); body1.setFont(font_);
body1.setCharacterSize(32); body1.setCharacterSize(32);
body1.setScale(0.5f, 0.5f); body1.setScale(0.5f, 0.5f);
body1.setFillColor(sf::Color(200, 200, 200)); body1.setFillColor(palette.overlayBody);
body1.setString(toSfStr(u8"SortLab визуализирует работу алгоритмов сортировки в реальном времени.\nКаждый столбик — это элемент массива. Высота = значение элемента.\nЦвета: серый — обычный, голубой — сравнение, оранжевый — перестановка, зелёный — готово.")); body1.setString(toSfStr(u8"SortLab визуализирует работу алгоритмов сортировки в реальном времени.\nКаждый столбик — это элемент массива. Высота = значение элемента.\nЦвета: серый — обычный, голубой — сравнение, оранжевый — перестановка, зелёный — готово."));
sf::Text section2; sf::Text section2;
section2.setFont(font_); section2.setFont(font_);
section2.setCharacterSize(42); section2.setCharacterSize(42);
section2.setScale(0.5f, 0.5f); section2.setScale(0.5f, 0.5f);
section2.setFillColor(sf::Color(0, 220, 255)); section2.setFillColor(palette.overlaySection);
section2.setString(toSfStr(u8"Алгоритмы и сложность")); section2.setString(toSfStr(u8"Алгоритмы и сложность"));
sf::Text body2; sf::Text body2;
body2.setFont(font_); body2.setFont(font_);
body2.setCharacterSize(32); body2.setCharacterSize(32);
body2.setScale(0.5f, 0.5f); body2.setScale(0.5f, 0.5f);
body2.setFillColor(sf::Color(200, 200, 200)); body2.setFillColor(palette.overlayBody);
body2.setString(toSfStr(u8"[1] Bubble Sort Время: O(n) / O(n^2) / O(n^2) Память: O(1)\n[2] Selection Sort Время: O(n^2) / O(n^2) / O(n^2) Память: O(1)\n[3] Insertion Sort Время: O(n) / O(n^2) / O(n^2) Память: O(1)\n[4] Merge Sort Время: O(n log n) / O(n log n) / O(n log n) Память: O(n)\n[5] Quick Sort Время: O(n log n) / O(n log n) / O(n^2) Память: O(log n)\n[6] Heap Sort Время: O(n log n) Память: O(1)\n[7] Shell Sort Время: O(n log^2 n) Память: O(1)\n[8] Radix Sort Время: O(n*k) Память: O(n + k)")); body2.setString(toSfStr(u8"[1] Bubble Sort Время: O(n) / O(n^2) / O(n^2) Память: O(1)\n[2] Selection Sort Время: O(n^2) / O(n^2) / O(n^2) Память: O(1)\n[3] Insertion Sort Время: O(n) / O(n^2) / O(n^2) Память: O(1)\n[4] Merge Sort Время: O(n log n) / O(n log n) / O(n log n) Память: O(n)\n[5] Quick Sort Время: O(n log n) / O(n log n) / O(n^2) Память: O(log n)\n[6] Heap Sort Время: O(n log n) Память: O(1)\n[7] Shell Sort Время: O(n log^2 n) Память: O(1)\n[8] Radix Sort Время: O(n*k) Память: O(n + k)"));
sf::Text section3; sf::Text section3;
section3.setFont(font_); section3.setFont(font_);
section3.setCharacterSize(42); section3.setCharacterSize(42);
section3.setScale(0.5f, 0.5f); section3.setScale(0.5f, 0.5f);
section3.setFillColor(sf::Color(0, 220, 255)); section3.setFillColor(palette.overlaySection);
section3.setString(toSfStr(u8"Гистограмма (внизу слева)")); section3.setString(toSfStr(u8"Гистограмма (внизу слева)"));
sf::Text body3; sf::Text body3;
body3.setFont(font_); body3.setFont(font_);
body3.setCharacterSize(32); body3.setCharacterSize(32);
body3.setScale(0.5f, 0.5f); body3.setScale(0.5f, 0.5f);
body3.setFillColor(sf::Color(200, 200, 200)); body3.setFillColor(palette.overlayBody);
body3.setString(toSfStr(u8"Показывает количество сравнений на каждом шаге сортировки.\nВысокий пик = много сравнений за один шаг.\nBubble Sort даёт ровную линию, Quick Sort — резкие пики в начале.")); body3.setString(toSfStr(u8"Показывает количество сравнений на каждом шаге сортировки.\nВысокий пик = много сравнений за один шаг.\nBubble Sort даёт ровную линию, Quick Sort — резкие пики в начале."));
sf::Text section4; sf::Text section4;
section4.setFont(font_); section4.setFont(font_);
section4.setCharacterSize(42); section4.setCharacterSize(42);
section4.setScale(0.5f, 0.5f); section4.setScale(0.5f, 0.5f);
section4.setFillColor(sf::Color(0, 220, 255)); section4.setFillColor(palette.overlaySection);
section4.setString(toSfStr(u8"Карта прогресса (внизу справа)")); section4.setString(toSfStr(u8"Карта прогресса (внизу справа)"));
sf::Text body4; sf::Text body4;
body4.setFont(font_); body4.setFont(font_);
body4.setCharacterSize(32); body4.setCharacterSize(32);
body4.setScale(0.5f, 0.5f); body4.setScale(0.5f, 0.5f);
body4.setFillColor(sf::Color(200, 200, 200)); body4.setFillColor(palette.overlayBody);
body4.setString(toSfStr(u8"Миниатюрная копия всего массива в виде сетки цветных квадратиков.\nПозволяет видеть глобальный прогресс сортировки даже при большом массиве.\nЗелёные квадраты = отсортированные элементы.")); body4.setString(toSfStr(u8"Миниатюрная копия всего массива в виде сетки цветных квадратиков.\nПозволяет видеть глобальный прогресс сортировки даже при большом массиве.\nЗелёные квадраты = отсортированные элементы."));
sf::Text footer; sf::Text footer;
footer.setFont(font_); footer.setFont(font_);
footer.setCharacterSize(30); footer.setCharacterSize(30);
footer.setScale(0.5f, 0.5f); footer.setScale(0.5f, 0.5f);
footer.setFillColor(sf::Color(100, 100, 100)); footer.setFillColor(palette.overlayFooter);
footer.setString(toSfStr(u8"Нажми [I] чтобы закрыть | [H] история | [Q] выход | [Space] старт/пауза | [R] перемешать")); footer.setString(toSfStr(u8"Нажми [I] чтобы закрыть | [H] история | [Q] выход | [Space] старт/пауза | [R] перемешать | [T] тема"));
float totalHeight = 0.0f; float totalHeight = 0.0f;
totalHeight += title.getGlobalBounds().height + 10.0f; totalHeight += title.getGlobalBounds().height + 10.0f;
@@ -328,6 +348,8 @@ void UI::drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history
return; return;
} }
const ColorPalette& palette = Theme::getInstance().getPalette();
auto toSfStr = [](const char* utf8) -> sf::String { auto toSfStr = [](const char* utf8) -> sf::String {
std::string s(utf8); std::string s(utf8);
return sf::String::fromUtf8(s.begin(), s.end()); return sf::String::fromUtf8(s.begin(), s.end());
@@ -338,14 +360,14 @@ void UI::drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history
sf::RectangleShape overlay(sf::Vector2f(windowWidth, windowHeight)); sf::RectangleShape overlay(sf::Vector2f(windowWidth, windowHeight));
overlay.setPosition(0.0f, 0.0f); overlay.setPosition(0.0f, 0.0f);
overlay.setFillColor(sf::Color(8, 10, 18, 230)); overlay.setFillColor(palette.overlayBackground);
window.draw(overlay); window.draw(overlay);
sf::Text title; sf::Text title;
title.setFont(font_); title.setFont(font_);
title.setCharacterSize(80); title.setCharacterSize(80);
title.setScale(0.5f, 0.5f); title.setScale(0.5f, 0.5f);
title.setFillColor(sf::Color::White); title.setFillColor(palette.overlayTitle);
title.setString(toSfStr(u8"История сортировок [H — закрыть]")); title.setString(toSfStr(u8"История сортировок [H — закрыть]"));
const auto& records = history.getRecords(); const auto& records = history.getRecords();
@@ -362,7 +384,7 @@ void UI::drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history
emptyText.setFont(font_); emptyText.setFont(font_);
emptyText.setCharacterSize(40); emptyText.setCharacterSize(40);
emptyText.setScale(0.5f, 0.5f); emptyText.setScale(0.5f, 0.5f);
emptyText.setFillColor(sf::Color(160, 160, 160)); emptyText.setFillColor(palette.overlaySubtitle);
emptyText.setString(toSfStr(u8"Нет завершённых сортировок")); emptyText.setString(toSfStr(u8"Нет завершённых сортировок"));
sf::FloatRect emptyBounds = emptyText.getGlobalBounds(); sf::FloatRect emptyBounds = emptyText.getGlobalBounds();
@@ -375,7 +397,7 @@ void UI::drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history
header.setFont(font_); header.setFont(font_);
header.setCharacterSize(36); header.setCharacterSize(36);
header.setScale(0.5f, 0.5f); header.setScale(0.5f, 0.5f);
header.setFillColor(sf::Color(0, 220, 255)); header.setFillColor(palette.historyTableHeader);
header.setString(toSfStr(u8"Алгоритм Размер Сравнений Свапов Скорость")); header.setString(toSfStr(u8"Алгоритм Размер Сравнений Свапов Скорость"));
float tableX = 100.0f; float tableX = 100.0f;
@@ -385,7 +407,7 @@ void UI::drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history
sf::RectangleShape separator(sf::Vector2f(windowWidth - 200.0f, 1.0f)); sf::RectangleShape separator(sf::Vector2f(windowWidth - 200.0f, 1.0f));
separator.setPosition(tableX, startY); separator.setPosition(tableX, startY);
separator.setFillColor(sf::Color(60, 70, 90)); separator.setFillColor(palette.separator);
window.draw(separator); window.draw(separator);
startY += 10.0f; startY += 10.0f;
@@ -400,11 +422,11 @@ void UI::drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history
rowBg.setPosition(tableX, startY); rowBg.setPosition(tableX, startY);
if (isNewest) { if (isNewest) {
rowBg.setFillColor(sf::Color(30, 60, 90, 150)); rowBg.setFillColor(palette.historyNewestHighlight);
} else if (displayCount % 2 == 1) { } else if (displayCount % 2 == 1) {
rowBg.setFillColor(sf::Color(15, 18, 25, 100)); rowBg.setFillColor(palette.historyRowOdd);
} else { } else {
rowBg.setFillColor(sf::Color(0, 0, 0, 0)); rowBg.setFillColor(palette.historyRowEven);
} }
window.draw(rowBg); window.draw(rowBg);
@@ -419,7 +441,7 @@ void UI::drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history
rowText.setFont(font_); rowText.setFont(font_);
rowText.setCharacterSize(32); rowText.setCharacterSize(32);
rowText.setScale(0.5f, 0.5f); rowText.setScale(0.5f, 0.5f);
rowText.setFillColor(isNewest ? sf::Color(100, 255, 150) : sf::Color(200, 200, 200)); rowText.setFillColor(isNewest ? palette.historyNewestText : palette.overlayBody);
rowText.setString(algoBuffer); rowText.setString(algoBuffer);
rowText.setPosition(tableX, startY + 2.0f); rowText.setPosition(tableX, startY + 2.0f);
window.draw(rowText); window.draw(rowText);
@@ -433,11 +455,11 @@ void UI::drawHistoryOverlay(sf::RenderWindow& window, const SortHistory& history
speedText.setScale(0.5f, 0.5f); speedText.setScale(0.5f, 0.5f);
if (record.relativeSpeed == 1.0f) { if (record.relativeSpeed == 1.0f) {
speedText.setFillColor(sf::Color(100, 255, 120)); speedText.setFillColor(palette.historySpeedFastest);
} else if (record.relativeSpeed >= 2.0f) { } else if (record.relativeSpeed >= 2.0f) {
speedText.setFillColor(sf::Color(255, 160, 60)); speedText.setFillColor(palette.historySpeedSlow);
} else { } else {
speedText.setFillColor(isNewest ? sf::Color(100, 255, 150) : sf::Color(200, 200, 200)); speedText.setFillColor(isNewest ? palette.historyNewestText : palette.overlayBody);
} }
speedText.setString(speedBuffer); speedText.setString(speedBuffer);