793 lines
37 KiB
HTML
793 lines
37 KiB
HTML
|
||
---
|
||
|
||
## 📄 `docs/index.html` (обновлённая HTML-документация)
|
||
|
||
```html
|
||
<!DOCTYPE html>
|
||
<html lang="ru">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>X11 GUI Library - Documentation</title>
|
||
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@300;400;600;700&family=JetBrains+Mono:wght@400;500&display=swap" rel="stylesheet">
|
||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/themes/prism-tomorrow.min.css">
|
||
<style>
|
||
:root {
|
||
--bg-primary: #0f172a;
|
||
--bg-secondary: #1e293b;
|
||
--bg-tertiary: #334155;
|
||
--text-primary: #f1f5f9;
|
||
--text-secondary: #94a3b8;
|
||
--accent: #38bdf8;
|
||
--accent-hover: #0ea5e9;
|
||
--success: #22c55e;
|
||
--warning: #f59e0b;
|
||
--danger: #ef4444;
|
||
--border: #334155;
|
||
--code-bg: #0d1117;
|
||
}
|
||
|
||
* { margin: 0; padding: 0; box-sizing: border-box; }
|
||
|
||
body {
|
||
font-family: 'Inter', sans-serif;
|
||
background-color: var(--bg-primary);
|
||
color: var(--text-primary);
|
||
line-height: 1.6;
|
||
display: flex;
|
||
min-height: 100vh;
|
||
}
|
||
|
||
::-webkit-scrollbar { width: 8px; height: 8px; }
|
||
::-webkit-scrollbar-track { background: var(--bg-primary); }
|
||
::-webkit-scrollbar-thumb { background: var(--bg-tertiary); border-radius: 4px; }
|
||
::-webkit-scrollbar-thumb:hover { background: var(--accent); }
|
||
|
||
.sidebar {
|
||
width: 280px;
|
||
background-color: var(--bg-secondary);
|
||
border-right: 1px solid var(--border);
|
||
position: fixed;
|
||
height: 100vh;
|
||
overflow-y: auto;
|
||
padding: 2rem 1.5rem;
|
||
z-index: 100;
|
||
}
|
||
|
||
.logo {
|
||
font-size: 1.5rem;
|
||
font-weight: 700;
|
||
color: var(--accent);
|
||
margin-bottom: 2rem;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 0.5rem;
|
||
}
|
||
|
||
.search-box {
|
||
width: 100%;
|
||
padding: 0.75rem;
|
||
background: var(--bg-primary);
|
||
border: 1px solid var(--border);
|
||
border-radius: 6px;
|
||
color: var(--text-primary);
|
||
font-family: 'Inter', sans-serif;
|
||
margin-bottom: 1.5rem;
|
||
outline: none;
|
||
transition: border-color 0.2s;
|
||
}
|
||
.search-box:focus { border-color: var(--accent); }
|
||
|
||
.nav-group { margin-bottom: 1.5rem; }
|
||
.nav-title {
|
||
font-size: 0.75rem;
|
||
text-transform: uppercase;
|
||
letter-spacing: 0.05em;
|
||
color: var(--text-secondary);
|
||
margin-bottom: 0.5rem;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.nav-link {
|
||
display: block;
|
||
padding: 0.5rem 0.75rem;
|
||
color: var(--text-secondary);
|
||
text-decoration: none;
|
||
border-radius: 6px;
|
||
font-size: 0.9rem;
|
||
transition: all 0.2s;
|
||
}
|
||
.nav-link:hover, .nav-link.active {
|
||
background-color: var(--bg-tertiary);
|
||
color: var(--accent);
|
||
}
|
||
|
||
.main-content {
|
||
margin-left: 280px;
|
||
flex: 1;
|
||
padding: 3rem 4rem;
|
||
max-width: 1200px;
|
||
}
|
||
|
||
h1 { font-size: 2.5rem; margin-bottom: 1rem; color: var(--text-primary); }
|
||
h2 { font-size: 1.8rem; margin-top: 3rem; margin-bottom: 1rem; color: var(--accent); border-bottom: 1px solid var(--border); padding-bottom: 0.5rem; }
|
||
h3 { font-size: 1.3rem; margin-top: 2rem; margin-bottom: 0.75rem; color: var(--text-primary); }
|
||
p { margin-bottom: 1rem; color: var(--text-secondary); }
|
||
|
||
.badge {
|
||
display: inline-block;
|
||
padding: 0.25rem 0.5rem;
|
||
background: var(--bg-tertiary);
|
||
color: var(--accent);
|
||
border-radius: 4px;
|
||
font-size: 0.75rem;
|
||
font-family: 'JetBrains Mono', monospace;
|
||
margin-right: 0.5rem;
|
||
}
|
||
.badge.new { background: var(--success); color: white; }
|
||
.badge.warning { background: var(--warning); color: black; }
|
||
|
||
.code-wrapper {
|
||
position: relative;
|
||
margin: 1.5rem 0;
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
border: 1px solid var(--border);
|
||
}
|
||
.code-header {
|
||
background: var(--bg-secondary);
|
||
padding: 0.5rem 1rem;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
border-bottom: 1px solid var(--border);
|
||
font-size: 0.85rem;
|
||
color: var(--text-secondary);
|
||
}
|
||
.copy-btn {
|
||
background: transparent;
|
||
border: 1px solid var(--border);
|
||
color: var(--text-secondary);
|
||
padding: 0.25rem 0.75rem;
|
||
border-radius: 4px;
|
||
cursor: pointer;
|
||
font-size: 0.8rem;
|
||
transition: all 0.2s;
|
||
}
|
||
.copy-btn:hover { background: var(--bg-tertiary); color: var(--text-primary); }
|
||
.copy-btn.copied { border-color: var(--success); color: var(--success); }
|
||
|
||
pre { margin: 0 !important; border-radius: 0 !important; background: var(--code-bg) !important; }
|
||
code { font-family: 'JetBrains Mono', monospace !important; font-size: 0.9rem !important; }
|
||
|
||
table {
|
||
width: 100%;
|
||
border-collapse: collapse;
|
||
margin: 1.5rem 0;
|
||
background: var(--bg-secondary);
|
||
border-radius: 8px;
|
||
overflow: hidden;
|
||
}
|
||
th, td {
|
||
padding: 1rem;
|
||
text-align: left;
|
||
border-bottom: 1px solid var(--border);
|
||
}
|
||
th { background: var(--bg-tertiary); color: var(--text-primary); font-weight: 600; }
|
||
td { color: var(--text-secondary); }
|
||
td code { background: var(--bg-primary); padding: 0.2rem 0.4rem; border-radius: 4px; color: var(--accent); }
|
||
|
||
details {
|
||
background: var(--bg-secondary);
|
||
border: 1px solid var(--border);
|
||
border-radius: 8px;
|
||
margin-bottom: 1rem;
|
||
overflow: hidden;
|
||
}
|
||
summary {
|
||
padding: 1rem;
|
||
cursor: pointer;
|
||
font-weight: 600;
|
||
color: var(--text-primary);
|
||
list-style: none;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
summary::-webkit-details-marker { display: none; }
|
||
summary::after { content: '+'; font-size: 1.5rem; color: var(--accent); }
|
||
details[open] summary::after { content: '−'; }
|
||
details[open] summary { border-bottom: 1px solid var(--border); }
|
||
.details-content { padding: 1.5rem; }
|
||
|
||
.highlight { color: var(--accent); font-weight: 600; }
|
||
|
||
.feature-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
||
gap: 1.5rem;
|
||
margin: 2rem 0;
|
||
}
|
||
.feature-card {
|
||
background: var(--bg-secondary);
|
||
padding: 1.5rem;
|
||
border-radius: 8px;
|
||
border: 1px solid var(--border);
|
||
transition: all 0.2s;
|
||
}
|
||
.feature-card:hover {
|
||
border-color: var(--accent);
|
||
transform: translateY(-2px);
|
||
}
|
||
.feature-card .icon {
|
||
font-size: 2rem;
|
||
margin-bottom: 0.5rem;
|
||
}
|
||
.feature-card h4 { color: var(--text-primary); margin-bottom: 0.5rem; }
|
||
|
||
.hotkey-grid {
|
||
display: grid;
|
||
grid-template-columns: 80px 1fr;
|
||
gap: 0.5rem;
|
||
margin: 1rem 0;
|
||
}
|
||
.hotkey-key {
|
||
background: var(--bg-tertiary);
|
||
color: var(--accent);
|
||
padding: 0.5rem 1rem;
|
||
border-radius: 6px;
|
||
font-family: 'JetBrains Mono', monospace;
|
||
text-align: center;
|
||
font-weight: 600;
|
||
}
|
||
|
||
.alert {
|
||
padding: 1rem;
|
||
border-radius: 8px;
|
||
margin: 1.5rem 0;
|
||
border-left: 4px solid;
|
||
}
|
||
.alert.success { background: rgba(34, 197, 94, 0.1); border-color: var(--success); }
|
||
.alert.warning { background: rgba(245, 158, 11, 0.1); border-color: var(--warning); }
|
||
.alert.info { background: rgba(56, 189, 248, 0.1); border-color: var(--accent); }
|
||
|
||
@media (max-width: 768px) {
|
||
.sidebar { transform: translateX(-100%); transition: transform 0.3s; }
|
||
.sidebar.open { transform: translateX(0); }
|
||
.main-content { margin-left: 0; padding: 2rem; }
|
||
}
|
||
</style>
|
||
</head>
|
||
<body>
|
||
|
||
<nav class="sidebar">
|
||
<div class="logo">
|
||
<svg width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2"><rect x="3" y="3" width="18" height="18" rx="2"/><path d="M3 9h18M9 21V9"/></svg>
|
||
X11 GUI Lib
|
||
</div>
|
||
<input type="text" class="search-box" placeholder="Поиск..." id="searchInput">
|
||
|
||
<div class="nav-group">
|
||
<div class="nav-title">Начало</div>
|
||
<a href="#intro" class="nav-link">Введение</a>
|
||
<a href="#features" class="nav-link">Возможности</a>
|
||
<a href="#quickstart" class="nav-link">Быстрый старт</a>
|
||
</div>
|
||
<div class="nav-group">
|
||
<div class="nav-title">Компоненты</div>
|
||
<a href="#eventbus" class="nav-link">EventBus</a>
|
||
<a href="#rawpicture" class="nav-link">RawPicture</a>
|
||
<a href="#widgets" class="nav-link">Виджеты</a>
|
||
</div>
|
||
<div class="nav-group">
|
||
<div class="nav-title">Оптимизация <span class="badge new">NEW</span></div>
|
||
<a href="#dirty-rects" class="nav-link">Dirty Rectangles</a>
|
||
<a href="#double-buffer" class="nav-link">Двойная буферизация</a>
|
||
</div>
|
||
<div class="nav-group">
|
||
<div class="nav-title">Отладка <span class="badge new">NEW</span></div>
|
||
<a href="#debug-hotkeys" class="nav-link">Горячие клавиши</a>
|
||
<a href="#debug-overlay" class="nav-link">Debug Overlay</a>
|
||
<a href="#debug-programmatic" class="nav-link">API отладки</a>
|
||
</div>
|
||
<div class="nav-group">
|
||
<div class="nav-title">API</div>
|
||
<a href="#api-window" class="nav-link">gui::window</a>
|
||
<a href="#api-page" class="nav-link">gui::page</a>
|
||
<a href="#api-combat" class="nav-link">gui::combat</a>
|
||
<a href="#api-widget" class="nav-link">gui::Widget</a>
|
||
</div>
|
||
</nav>
|
||
|
||
<main class="main-content">
|
||
<section id="intro">
|
||
<h1>📘 X11 GUI Library</h1>
|
||
<p>Современная легковесная GUI библиотека для C++ с поддержкой X11, оптимизированная для производительности через систему частичной перерисовки и предоставляющая профессиональные инструменты отладки уровня Android Studio.</p>
|
||
|
||
<div class="alert info">
|
||
<strong>💡 Версия 2.0</strong> - добавлены: Dirty Rectangles, Android-style отладка, двойная буферизация, маркеры кликов, визуализация границ виджетов.
|
||
</div>
|
||
</section>
|
||
|
||
<section id="features">
|
||
<h2>✨ Возможности</h2>
|
||
<div class="feature-grid">
|
||
<div class="feature-card">
|
||
<div class="icon">⚡</div>
|
||
<h4>Dirty Rectangles</h4>
|
||
<p>Перерисовка только изменённых областей. Ускорение в 5-50 раз.</p>
|
||
</div>
|
||
<div class="feature-card">
|
||
<div class="icon">🎨</div>
|
||
<h4>Двойная буферизация</h4>
|
||
<p>Никакого мерцания благодаря backbuffer через X11 Pixmap.</p>
|
||
</div>
|
||
<div class="feature-card">
|
||
<div class="icon">🔍</div>
|
||
<h4>Android Debug</h4>
|
||
<p>Визуализация dirty rects, границ виджетов, маркеры кликов.</p>
|
||
</div>
|
||
<div class="feature-card">
|
||
<div class="icon">📡</div>
|
||
<h4>EventBus</h4>
|
||
<p>Слабое связывание через глобальную шину событий.</p>
|
||
</div>
|
||
<div class="feature-card">
|
||
<div class="icon">🎭</div>
|
||
<h4>Анимации</h4>
|
||
<p>Плавные dropdown и progress bar с ease-функциями.</p>
|
||
</div>
|
||
<div class="feature-card">
|
||
<div class="icon">📦</div>
|
||
<h4>Иерархия</h4>
|
||
<p>TreeView, ScrollView, вложенные виджеты.</p>
|
||
</div>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="quickstart">
|
||
<h2>🚀 Быстрый старт</h2>
|
||
<div class="code-wrapper">
|
||
<div class="code-header">
|
||
<span>main.cpp</span>
|
||
<button class="copy-btn" onclick="copyCode(this)">Копировать</button>
|
||
</div>
|
||
<pre><code class="language-cpp">#include "gui.h"
|
||
|
||
int main() {
|
||
gui::window win("My App");
|
||
|
||
win.addPAGE("main");
|
||
win.configur_page("main", [](gui::page& p) {
|
||
gui::WidgetData btn;
|
||
btn.id = "start_btn";
|
||
btn.text = "Start";
|
||
btn.x = 50; btn.y = 50;
|
||
btn.width = 150; btn.height = 40;
|
||
btn.bg_color = 0x2563EB;
|
||
p.Button(btn);
|
||
}, true);
|
||
|
||
// Включить все режимы отладки
|
||
win.debug().show_debug_overlay = true;
|
||
win.debug().show_dirty_rects = true;
|
||
win.debug().log_to_console = true;
|
||
|
||
gui::EventBus::get().on("button.click", [](const gui::EventData& e) {
|
||
std::cout << "Clicked: " << e.source_id << "\n";
|
||
});
|
||
|
||
while (true) {
|
||
win.update();
|
||
}
|
||
return 0;
|
||
}</code></pre>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="dirty-rects">
|
||
<h2>⚡ Dirty Rectangles <span class="badge new">NEW</span></h2>
|
||
<p>Система частичной перерисовки <code>DirtyRegion</code> отслеживает только изменённые области экрана. Когда виджет меняет состояние, он помечает свою область как "грязную", и библиотека перерисовывает только эти области.</p>
|
||
|
||
<h3>Как это работает</h3>
|
||
<ol style="margin-left: 2rem; color: var(--text-secondary);">
|
||
<li>Виджет вызывает <code>mark_dirty()</code> при изменении</li>
|
||
<li>Область добавляется в <code>DirtyRegion</code></li>
|
||
<li>При <code>window::update()</code> перерисовываются только dirty-области</li>
|
||
<li><code>XCopyArea</code> копирует только нужные регионы из backbuffer</li>
|
||
</ol>
|
||
|
||
<div class="alert success">
|
||
<strong>📊 Результат:</strong> Для интерфейса с 100 виджетами, где меняется 1 элемент, CPU нагрузка снижается в 50+ раз.
|
||
</div>
|
||
|
||
<h3>Автоматическое отслеживание</h3>
|
||
<p>Библиотека автоматически помечает как dirty:</p>
|
||
<ul style="margin-left: 2rem; color: var(--text-secondary);">
|
||
<li><strong>Dropdown</strong> - во время анимации открытия/закрытия</li>
|
||
<li><strong>Progressbar</strong> - при изменении значения или в indeterminate-режиме</li>
|
||
<li><strong>Slider</strong> - во время drag</li>
|
||
<li><strong>Input</strong> - при вводе текста</li>
|
||
<li><strong>Expose events</strong> - при восстановлении окна из-под другого</li>
|
||
<li><strong>Resize</strong> - весь экран при изменении размера окна</li>
|
||
</ul>
|
||
</section>
|
||
|
||
<section id="double-buffer">
|
||
<h2>🎨 Двойная буферизация</h2>
|
||
<p>Вся отрисовка происходит в off-screen <code>Pixmap</code> (backbuffer), затем нужные области копируются в окно через <code>XCopyArea</code>. Это полностью убирает мерцание при анимациях.</p>
|
||
|
||
<div class="code-wrapper">
|
||
<div class="code-header"><span>Как работает backbuffer</span></div>
|
||
<pre><code class="language-cpp">// 1. Создаём backbuffer при инициализации
|
||
backbuffer = XCreatePixmap(dpy, win, width, height, depth);
|
||
|
||
// 2. Вся отрисовка идёт в backbuffer
|
||
XFillRectangle(dpy, backbuffer, gc, ...);
|
||
XDrawString(dpy, backbuffer, gc, ...);
|
||
|
||
// 3. Копируем только dirty-регионы в окно
|
||
for (auto& rect : dirty_rects) {
|
||
XCopyArea(dpy, backbuffer, window, gc,
|
||
rect.x, rect.y, rect.width, rect.height,
|
||
rect.x, rect.y);
|
||
}</code></pre>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="debug-hotkeys">
|
||
<h2>⌨️ Горячие клавиши отладки <span class="badge new">NEW</span></h2>
|
||
<p>Во время работы приложения используйте F-клавиши для переключения режимов отладки:</p>
|
||
|
||
<div class="hotkey-grid">
|
||
<div class="hotkey-key">F1</div>
|
||
<div>Toggle <strong>Debug Overlay</strong> - FPS, координаты мыши, лог событий</div>
|
||
|
||
<div class="hotkey-key">F2</div>
|
||
<div>Toggle <strong>Dirty Rectangles</strong> - мигание изменённых областей (Android-style)</div>
|
||
|
||
<div class="hotkey-key">F3</div>
|
||
<div>Toggle <strong>Click Markers</strong> - красные крестики в местах клика</div>
|
||
|
||
<div class="hotkey-key">F4</div>
|
||
<div>Toggle <strong>Widget Bounds</strong> - розовые границы всех виджетов</div>
|
||
|
||
<div class="hotkey-key">F5</div>
|
||
<div>Toggle <strong>Console Logging</strong> - вывод событий в stdout</div>
|
||
</div>
|
||
|
||
<div class="alert warning">
|
||
<strong>💡 Совет:</strong> Включите F2 при разработке - вы сразу увидите, какие области перерисовываются и сможете оптимизировать UI.
|
||
</div>
|
||
</section>
|
||
|
||
<section id="debug-overlay">
|
||
<h2>📊 Debug Overlay</h2>
|
||
<p>При нажатии <kbd>F1</kbd> в правом верхнем углу появляется панель с информацией в реальном времени:</p>
|
||
|
||
<table>
|
||
<thead>
|
||
<tr><th>Показатель</th><th>Описание</th></tr>
|
||
</thead>
|
||
<tbody>
|
||
<tr><td><code>FPS</code></td><td>Текущий FPS (обновляется каждые 0.5 сек)</td></tr>
|
||
<tr><td><code>Widgets</code></td><td>Общее количество виджетов на всех страницах</td></tr>
|
||
<tr><td><code>Partial updates</code></td><td>Счётчик частичных обновлений (показывает эффективность dirty rects)</td></tr>
|
||
<tr><td><code>Mouse</code></td><td>Текущие координаты мыши</td></tr>
|
||
<tr><td><code>Event Log</code></td><td>Последние 5 событий с полной информацией</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
|
||
<section id="debug-programmatic">
|
||
<h2>🔧 API отладки</h2>
|
||
<div class="code-wrapper">
|
||
<div class="code-header"><span>Программное управление</span></div>
|
||
<pre><code class="language-cpp">gui::window win("App");
|
||
|
||
// Прямой доступ к настройкам отладки
|
||
win.debug().show_debug_overlay = true;
|
||
win.debug().show_dirty_rects = true;
|
||
win.debug().show_click_markers = true;
|
||
win.debug().show_widget_bounds = true;
|
||
win.debug().log_to_console = true;
|
||
|
||
// Увеличить размер лога
|
||
win.debug().max_log_size = 30;
|
||
|
||
// Ручное управление dirty region
|
||
win.mark_dirty(100, 100, 200, 50); // пометить область
|
||
win.mark_all_dirty(); // пометить весь экран
|
||
|
||
// Чтение состояния отладки
|
||
std::cout << "FPS: " << win.debug().fps << "\n";
|
||
std::cout << "Mouse: " << win.debug().mouse_x
|
||
<< "," << win.debug().mouse_y << "\n";</code></pre>
|
||
</div>
|
||
|
||
<h3>Структура DebugInfo</h3>
|
||
<div class="code-wrapper">
|
||
<pre><code class="language-cpp">struct DebugInfo {
|
||
int fps; // текущий FPS
|
||
int widget_count; // количество виджетов
|
||
int partial_updates; // счётчик dirty updates
|
||
int mouse_x, mouse_y; // координаты мыши
|
||
|
||
bool show_debug_overlay; // F1
|
||
bool show_dirty_rects; // F2
|
||
bool show_click_markers; // F3
|
||
bool show_widget_bounds; // F4
|
||
bool log_to_console; // F5
|
||
|
||
std::deque<std::string> log; // лог событий
|
||
size_t max_log_size; // макс размер лога
|
||
};</code></pre>
|
||
</div>
|
||
</section>
|
||
|
||
<section id="eventbus">
|
||
<h2>📡 EventBus</h2>
|
||
<p>Глобальная шина событий для слабой связи между компонентами. Поддерживает глобальное логирование всех событий.</p>
|
||
|
||
<div class="code-wrapper">
|
||
<div class="code-header"><span>Работа с EventBus</span></div>
|
||
<pre><code class="language-cpp">// Подписка
|
||
int token = gui::EventBus::get().on("button.click", [](const gui::EventData& e) {
|
||
std::cout << "Button: " << e.source_id << "\n";
|
||
});
|
||
|
||
// Отправка
|
||
gui::EventBus::get().emit("custom.event", {
|
||
"widget_id", // source_id
|
||
"text value", // text
|
||
0.5f, // value
|
||
3, // index
|
||
true // flag
|
||
});
|
||
|
||
// Отписка
|
||
gui::EventBus::get().off(token);</code></pre>
|
||
</div>
|
||
|
||
<h3>Встроенные события</h3>
|
||
<table>
|
||
<thead><tr><th>Событие</th><th>Когда возникает</th><th>Данные</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>button.click</code></td><td>Нажатие кнопки</td><td>source_id, text</td></tr>
|
||
<tr><td><code>checkbox.change</code></td><td>Изменение чекбокса</td><td>source_id, text, flag</td></tr>
|
||
<tr><td><code>radio.change</code></td><td>Выбор радио-кнопки</td><td>source_id, text, flag</td></tr>
|
||
<tr><td><code>slider.change</code></td><td>Движение слайдера</td><td>source_id, value (0-1)</td></tr>
|
||
<tr><td><code>list.select</code></td><td>Выбор в списке</td><td>source_id, text, index</td></tr>
|
||
<tr><td><code>dropdown.select</code></td><td>Выбор в dropdown</td><td>source_id, text, index</td></tr>
|
||
<tr><td><code>tree.toggle</code></td><td>Раскрытие узла</td><td>source_id, text, flag</td></tr>
|
||
<tr><td><code>input.submit</code></td><td>Enter в Input</td><td>source_id, text</td></tr>
|
||
<tr><td><code>page.open</code></td><td>Открытие страницы</td><td>source_id (name)</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
|
||
<section id="rawpicture">
|
||
<h2>🎨 RawPicture</h2>
|
||
<p>Класс для программного рендеринга графики. Поддерживает 8/16/24/32 бита, загрузку BMP и PPM, и все базовые примитивы.</p>
|
||
|
||
<div class="code-wrapper">
|
||
<div class="code-header"><span>Пример использования</span></div>
|
||
<pre><code class="language-cpp">auto pic = std::make_shared<gui::RawPicture>(200, 200);
|
||
|
||
pic->clear(0x1A1A1A);
|
||
pic->fillCircle(100, 100, 50, 0xFF0000);
|
||
pic->drawText(display, 80, 95, "Hello", 0xFFFFFF);
|
||
|
||
gui::WidgetData w;
|
||
w.id = "image";
|
||
w.x = 50; w.y = 150;
|
||
w.width = 200; w.height = 200;
|
||
w.picture = pic;
|
||
page.RawPictureWidget(w);</code></pre>
|
||
</div>
|
||
|
||
<h3>Доступные примитивы</h3>
|
||
<table>
|
||
<thead><tr><th>Метод</th><th>Описание</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>clear(color)</code></td><td>Очистка цветом</td></tr>
|
||
<tr><td><code>drawLine(x0,y0,x1,y1,c)</code></td><td>Линия (алгоритм Брезенхема)</td></tr>
|
||
<tr><td><code>drawRect/fillRect(...)</code></td><td>Прямоугольник</td></tr>
|
||
<tr><td><code>drawCircle/fillCircle(...)</code></td><td>Окружность/круг</td></tr>
|
||
<tr><td><code>drawTriangle/fillTriangle(...)</code></td><td>Треугольник</td></tr>
|
||
<tr><td><code>drawPolygon/fillPolygon(...)</code></td><td>Произвольный полигон</td></tr>
|
||
<tr><td><code>drawText(...)</code></td><td>Текст (X11 шрифт с масштабированием)</td></tr>
|
||
<tr><td><code>load(path)</code></td><td>Загрузка BMP или PPM</td></tr>
|
||
<tr><td><code>blit(src,dx,dy)</code></td><td>Копирование из другого RawPicture</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
|
||
<section id="widgets">
|
||
<h2>🧩 Виджеты</h2>
|
||
<p>Все виджеты создаются через фабричные методы класса <code>combat</code> с передачей структуры <code>WidgetData</code>.</p>
|
||
|
||
<table>
|
||
<thead><tr><th>Метод</th><th>Тип</th><th>Описание</th></tr></thead>
|
||
<tbody>
|
||
<tr><td><code>Button(d)</code></td><td>BUTTON</td><td>Кнопка</td></tr>
|
||
<tr><td><code>Label(d)</code></td><td>LABEL</td><td>Текстовая метка</td></tr>
|
||
<tr><td><code>Input(d)</code></td><td>INPUT</td><td>Поле ввода</td></tr>
|
||
<tr><td><code>Checkbox(d)</code></td><td>CHECKBOX</td><td>Флажок</td></tr>
|
||
<tr><td><code>Radiobutton(d)</code></td><td>RADIO</td><td>Радио-кнопка</td></tr>
|
||
<tr><td><code>Slider(d)</code></td><td>SLIDER</td><td>Ползунок 0-1</td></tr>
|
||
<tr><td><code>Progressbar(d)</code></td><td>PROGRESS</td><td>Прогресс-бар</td></tr>
|
||
<tr><td><code>Listbox(d)</code></td><td>LISTBOX</td><td>Список</td></tr>
|
||
<tr><td><code>Dropdown(d)</code></td><td>DROPDOWN</td><td>Выпадающий список</td></tr>
|
||
<tr><td><code>TreeView(d)</code></td><td>TREE_VIEW</td><td>Дерево</td></tr>
|
||
<tr><td><code>TreeNode(d)</code></td><td>TREE</td><td>Узел дерева</td></tr>
|
||
<tr><td><code>ScrollView(d)</code></td><td>SCROLL</td><td>Контейнер с прокруткой</td></tr>
|
||
<tr><td><code>RawPictureWidget(d)</code></td><td>RAW_PIC</td><td>Изображение</td></tr>
|
||
<tr><td><code>SpriteButton(d)</code></td><td>SPRITE_BTN</td><td>Кнопка со спрайтом</td></tr>
|
||
</tbody>
|
||
</table>
|
||
</section>
|
||
|
||
<section id="api-window">
|
||
<h2>📚 API: gui::window</h2>
|
||
<details>
|
||
<summary><code>window(std::string title)</code></summary>
|
||
<div class="details-content">
|
||
<p>Конструктор. Создаёт X11 окно 1200x600, инициализирует backbuffer, настраивает глобальный логгер EventBus.</p>
|
||
</div>
|
||
</details>
|
||
<details>
|
||
<summary><code>void update()</code></summary>
|
||
<div class="details-content">
|
||
<p>Главный метод цикла. Обрабатывает X11 события, обновляет анимации, перерисовывает dirty regions, обновляет debug overlay. Должен вызываться постоянно.</p>
|
||
</div>
|
||
</details>
|
||
<details>
|
||
<summary><code>void addPAGE(name)</code> / <code>configur_page(...)</code></summary>
|
||
<div class="details-content">
|
||
<p>Создание и настройка страниц с виджетами через callback.</p>
|
||
</div>
|
||
</details>
|
||
<details>
|
||
<summary><code>void openPAGE(name)</code></summary>
|
||
<div class="details-content">
|
||
<p>Переключение активной страницы. Помечает весь экран как dirty.</p>
|
||
</div>
|
||
</details>
|
||
<details>
|
||
<summary><code>DebugInfo& debug()</code> <span class="badge new">NEW</span></summary>
|
||
<div class="details-content">
|
||
<p>Доступ к отладочной информации: FPS, координаты, режимы, лог событий.</p>
|
||
</div>
|
||
</details>
|
||
<details>
|
||
<summary><code>void mark_dirty(x,y,w,h)</code> / <code>mark_all_dirty()</code> <span class="badge new">NEW</span></summary>
|
||
<div class="details-content">
|
||
<p>Ручное управление dirty region для принудительной перерисовки.</p>
|
||
</div>
|
||
</details>
|
||
</section>
|
||
|
||
<section id="api-page">
|
||
<h2>📚 API: gui::page</h2>
|
||
<p>Наследуется от <code>combat</code>, предоставляет все фабричные методы для создания виджетов. Имеет поля <code>width</code>, <code>height</code>, <code>active</code>.</p>
|
||
</section>
|
||
|
||
<section id="api-combat">
|
||
<h2>📚 API: gui::combat</h2>
|
||
<details>
|
||
<summary>Фабричные методы</summary>
|
||
<div class="details-content">
|
||
<p>14 методов для создания виджетов: Button, Label, Input, Checkbox, Radiobutton, Slider, Progressbar, Listbox, Dropdown, TreeView, TreeNode, ScrollView, RawPictureWidget, SpriteButton.</p>
|
||
</div>
|
||
</details>
|
||
<details>
|
||
<summary><code>Widget* find(const std::string& id)</code></summary>
|
||
<div class="details-content">
|
||
<p>Рекурсивный поиск виджета по ID в иерархии.</p>
|
||
</div>
|
||
</details>
|
||
<details>
|
||
<summary><code>void visit(fn)</code></summary>
|
||
<div class="details-content">
|
||
<p>Обход всех виджетов с применением лямбда-функции.</p>
|
||
</div>
|
||
</details>
|
||
<details>
|
||
<summary><code>int count_widgets()</code> <span class="badge new">NEW</span></summary>
|
||
<div class="details-content">
|
||
<p>Рекурсивный подсчёт всех виджетов в иерархии.</p>
|
||
</div>
|
||
</details>
|
||
</section>
|
||
|
||
<section id="api-widget">
|
||
<h2>📚 API: gui::Widget</h2>
|
||
<details>
|
||
<summary><code>void setValue(float v)</code> / <code>getValue()</code></summary>
|
||
<div class="details-content">
|
||
<p>Установка целевого значения с плавной анимацией перехода.</p>
|
||
</div>
|
||
</details>
|
||
<details>
|
||
<summary><code>void mark_dirty(dirty, ox, oy)</code> <span class="badge new">NEW</span></summary>
|
||
<div class="details-content">
|
||
<p>Помечает область виджета как dirty для частичной перерисовки.</p>
|
||
</div>
|
||
</details>
|
||
<details>
|
||
<summary>Автоматическая оптимизация</summary>
|
||
<div class="details-content">
|
||
<p>При изменении состояния виджет сам помечает свою область. Для составных виджетов (dropdown с popup) помечаются все связанные области.</p>
|
||
</div>
|
||
</details>
|
||
</section>
|
||
|
||
<footer style="margin-top: 4rem; padding-top: 2rem; border-top: 1px solid var(--border); color: var(--text-secondary); text-align: center; font-size: 0.9rem;">
|
||
<p>X11 GUI Library v2.0 • Made with ❤️ for KoDer</p>
|
||
<p style="margin-top: 0.5rem;">GitLab: <a href="https://git.bipfr.ru/KoDer/gui" style="color: var(--accent);">git.bipfr.ru/KoDer/gui</a></p>
|
||
</footer>
|
||
</main>
|
||
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/prism.min.js"></script>
|
||
<script src="https://cdnjs.cloudflare.com/ajax/libs/prism/1.29.0/components/prism-cpp.min.js"></script>
|
||
<script>
|
||
function copyCode(button) {
|
||
const codeBlock = button.parentElement.nextElementSibling.querySelector('code');
|
||
const text = codeBlock.innerText;
|
||
navigator.clipboard.writeText(text).then(() => {
|
||
const originalText = button.innerText;
|
||
button.innerText = '✓ Скопировано!';
|
||
button.classList.add('copied');
|
||
setTimeout(() => {
|
||
button.innerText = originalText;
|
||
button.classList.remove('copied');
|
||
}, 2000);
|
||
});
|
||
}
|
||
|
||
const searchInput = document.getElementById('searchInput');
|
||
const sections = document.querySelectorAll('section');
|
||
const navLinks = document.querySelectorAll('.nav-link');
|
||
|
||
searchInput.addEventListener('input', (e) => {
|
||
const term = e.target.value.toLowerCase();
|
||
sections.forEach(section => {
|
||
const text = section.innerText.toLowerCase();
|
||
section.style.display = (term === '' || text.includes(term)) ? 'block' : 'none';
|
||
});
|
||
navLinks.forEach(link => {
|
||
const targetId = link.getAttribute('href').substring(1);
|
||
const targetSection = document.getElementById(targetId);
|
||
link.style.display = (targetSection && targetSection.style.display === 'none') ? 'none' : 'block';
|
||
});
|
||
});
|
||
|
||
navLinks.forEach(link => {
|
||
link.addEventListener('click', (e) => {
|
||
navLinks.forEach(l => l.classList.remove('active'));
|
||
link.classList.add('active');
|
||
});
|
||
});
|
||
|
||
window.addEventListener('scroll', () => {
|
||
let current = '';
|
||
sections.forEach(section => {
|
||
if (section.style.display !== 'none') {
|
||
const sectionTop = section.offsetTop;
|
||
if (pageYOffset >= sectionTop - 100) {
|
||
current = section.getAttribute('id');
|
||
}
|
||
}
|
||
});
|
||
navLinks.forEach(link => {
|
||
link.classList.remove('active');
|
||
if (link.getAttribute('href').substring(1) === current) {
|
||
link.classList.add('active');
|
||
}
|
||
});
|
||
});
|
||
</script>
|
||
</body>
|
||
</html> |