Добавлена базовая структура движка.
This commit is contained in:
parent
405a6e7ead
commit
717f9f2b82
3 changed files with 312 additions and 0 deletions
119
source/ncui/engine/action.d
Normal file
119
source/ncui/engine/action.d
Normal file
|
|
@ -0,0 +1,119 @@
|
|||
/**
|
||||
* Команды, которые экран возвращает движку.
|
||||
*/
|
||||
module ncui.engine.action;
|
||||
|
||||
import ncui.engine.screen;
|
||||
|
||||
/**
|
||||
* Тип команды, которую экран возвращает движку.
|
||||
*
|
||||
* `ActionKind` описывает, что именно движок должен сделать после обработки ввода:
|
||||
* изменить стек экранов, сменить тему, завершить приложение и т.д.
|
||||
*/
|
||||
enum ActionKind
|
||||
{
|
||||
// Ничего не делать.
|
||||
None,
|
||||
// Добавить новый экран поверх текущего.
|
||||
Push,
|
||||
// Заменить верхний экран стека новым.
|
||||
Replace,
|
||||
// Удалить один или несколько экранов с вершины стека.
|
||||
Pop,
|
||||
// Завершить выполнение UI-цикла.
|
||||
Quit
|
||||
}
|
||||
|
||||
/**
|
||||
* Базовые типы результата экрана.
|
||||
*/
|
||||
enum ScreenKind
|
||||
{
|
||||
// Результат отсутствует или не имеет специальной семантики.
|
||||
None,
|
||||
// Отмена действия.
|
||||
Cancel
|
||||
}
|
||||
|
||||
/**
|
||||
* Результат работы экрана.
|
||||
*/
|
||||
struct ScreenResult
|
||||
{
|
||||
// Общий тип результата
|
||||
ScreenKind kind;
|
||||
|
||||
static ScreenResult none()
|
||||
{
|
||||
return ScreenResult(ScreenKind.None);
|
||||
}
|
||||
|
||||
static ScreenResult cancel()
|
||||
{
|
||||
return ScreenResult(ScreenKind.Cancel);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Действие, которое возвращает экран.
|
||||
*/
|
||||
struct ScreenAction
|
||||
{
|
||||
// Тип действия.
|
||||
ActionKind kind;
|
||||
// Следующий экран (используется для `Push` и `Replace`).
|
||||
IScreen next;
|
||||
// Результат (используется для `Pop`, `Quit`).
|
||||
ScreenResult result;
|
||||
/**
|
||||
* Ничего не делать.
|
||||
*
|
||||
* Возвращается, если стек и состояние движка менять не требуется.
|
||||
*/
|
||||
static ScreenAction none()
|
||||
{
|
||||
return ScreenAction(ActionKind.None, null, ScreenResult.none());
|
||||
}
|
||||
/**
|
||||
* Добавить новый экран поверх текущего.
|
||||
*
|
||||
* Params:
|
||||
* - screen: создаваемый экран.
|
||||
*/
|
||||
static ScreenAction push(IScreen screen)
|
||||
{
|
||||
// assert(isPointer!(typeof(result)), "ncuiNotNull expects a function that returns a pointer.");
|
||||
return ScreenAction(ActionKind.Push, screen, ScreenResult.none());
|
||||
}
|
||||
/**
|
||||
* Заменить верхний экран стека новым.
|
||||
*
|
||||
* Params:
|
||||
* - screen: создаваемый экран (не должен быть `null`).
|
||||
*/
|
||||
static ScreenAction replace(IScreen screen)
|
||||
{
|
||||
return ScreenAction(ActionKind.Replace, screen, ScreenResult.none());
|
||||
}
|
||||
/**
|
||||
* Закрыть верхний экран и передать результат родителю.
|
||||
*
|
||||
* Params:
|
||||
* - result: результат закрываемого экрана.
|
||||
*/
|
||||
static ScreenAction pop(ScreenResult result)
|
||||
{
|
||||
return ScreenAction(ActionKind.Pop, null, result);
|
||||
}
|
||||
/**
|
||||
* Завершить UI-цикл и вернуть финальный результат наружу.
|
||||
*
|
||||
* Params:
|
||||
* - result: финальный результат приложения (возвращается из `NCUI.run()`).
|
||||
*/
|
||||
static ScreenAction quit(ScreenResult result)
|
||||
{
|
||||
return ScreenAction(ActionKind.Quit, null, result);
|
||||
}
|
||||
}
|
||||
107
source/ncui/engine/ncui.d
Normal file
107
source/ncui/engine/ncui.d
Normal file
|
|
@ -0,0 +1,107 @@
|
|||
/**
|
||||
* Главный движок: стек экранов + цикл ввода.
|
||||
*/
|
||||
module ncui.engine.ncui;
|
||||
|
||||
import core.stdc.stdlib : EXIT_SUCCESS, EXIT_FAILURE, exit;
|
||||
import std.array : popBack;
|
||||
|
||||
import ncui.lib.logger;
|
||||
import ncui.core.session;
|
||||
import ncui.engine.screen;
|
||||
import ncui.engine.action;
|
||||
|
||||
final class NCUI
|
||||
{
|
||||
private:
|
||||
// Корневая сессия ncurses.
|
||||
Session _session;
|
||||
// Контекст выполнения действующей сессии.
|
||||
ScreenContext _context;
|
||||
// Стек экранов.
|
||||
IScreen[] _stack;
|
||||
// Флаг активности работы движка.
|
||||
bool _running;
|
||||
// Конечный результат выполнения.
|
||||
ScreenResult _result;
|
||||
|
||||
void apply(ScreenAction action)
|
||||
{
|
||||
while (_running && action.kind != ActionKind.None)
|
||||
{
|
||||
final switch (action.kind)
|
||||
{
|
||||
case ActionKind.Push:
|
||||
_session.clear();
|
||||
_stack ~= action.next;
|
||||
action = action.next.onShow(_context);
|
||||
break;
|
||||
|
||||
case ActionKind.Replace:
|
||||
if (_stack.length != 0)
|
||||
{
|
||||
_stack[$ - 1].close();
|
||||
_stack.popBack();
|
||||
}
|
||||
_session.clear();
|
||||
_stack ~= action.next;
|
||||
action = action.next.onShow(_context);
|
||||
break;
|
||||
|
||||
case ActionKind.Pop:
|
||||
break;
|
||||
|
||||
case ActionKind.Quit:
|
||||
_result = action.result;
|
||||
_running = false;
|
||||
return;
|
||||
|
||||
case ActionKind.None:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
this(const SessionConfig config = SessionConfig.init)
|
||||
{
|
||||
try
|
||||
{
|
||||
_session = new Session(config);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
error("Failed to initialize the session: ", e.msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
_context = ScreenContext(_session);
|
||||
}
|
||||
|
||||
ScreenResult run(IScreen screen)
|
||||
{
|
||||
// Пометить работу движка активным.
|
||||
_running = true;
|
||||
// Положить первый экран в стек для начала работы.
|
||||
apply(ScreenAction.push(screen));
|
||||
|
||||
while (_running && _stack.length != 0)
|
||||
{
|
||||
// Взять из стека последний экран.
|
||||
auto currentScreen = _stack[$ - 1];
|
||||
// Ожидать события нажатия клавиш в извлеченном из стека экране.
|
||||
auto event = _session.readKey(currentScreen.inputWindow());
|
||||
// Обработать нажатие клавиши в текущем окне.
|
||||
auto action = currentScreen.handle(_context, event);
|
||||
// Обработать возвращенное действие из окна.
|
||||
apply(action);
|
||||
}
|
||||
|
||||
// Завершить сессию ncurses.
|
||||
_session.close();
|
||||
|
||||
info("Engine successfully stopped");
|
||||
|
||||
return _result;
|
||||
}
|
||||
}
|
||||
86
source/ncui/engine/screen.d
Normal file
86
source/ncui/engine/screen.d
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
/**
|
||||
* Контракты экранов (screen) и контекст выполнения.
|
||||
*/
|
||||
module ncui.engine.screen;
|
||||
|
||||
import ncui.core.session;
|
||||
import ncui.core.event;
|
||||
import ncui.core.ncwin;
|
||||
import ncui.core.window;
|
||||
|
||||
import ncui.engine.action;
|
||||
|
||||
/**
|
||||
* Контекст выполнения экрана.
|
||||
*/
|
||||
struct ScreenContext
|
||||
{
|
||||
Session session;
|
||||
|
||||
this(Session s)
|
||||
{
|
||||
session = s;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Базовый интерфейс экрана.
|
||||
*
|
||||
* Правила:
|
||||
* - `onShow` должен нарисовать экран.
|
||||
* - `handle` обрабатывает ввод.
|
||||
* - `inputWindow` говорит движку, из какого окна читать ввод.
|
||||
* - `close` освобождает ресурс.
|
||||
*/
|
||||
interface IScreen
|
||||
{
|
||||
// Вызывается движком, когда экран становится активным (оказался наверху стека)
|
||||
// или когда движок явно инициирует перерисовку (зависит от реализации).
|
||||
ScreenAction onShow(ScreenContext context);
|
||||
// Вызывается движком после закрытия дочернего экрана (Pop/PopTo),
|
||||
// чтобы передать родителю результат дочернего экрана.
|
||||
ScreenAction onChildResult(ScreenContext context, ScreenResult child);
|
||||
// Обработка события ввода.
|
||||
// Вызывается движком для активного экрана при получении события клавиатуры.
|
||||
ScreenAction handle(ScreenContext context, KeyEvent event);
|
||||
|
||||
// Окно, из которого движок должен читать ввод для этого экрана.
|
||||
NCWin inputWindow();
|
||||
|
||||
// Освобождение ресурсов экрана.
|
||||
void close();
|
||||
}
|
||||
|
||||
abstract class ScreenBase : IScreen
|
||||
{
|
||||
protected:
|
||||
Window _window;
|
||||
|
||||
public:
|
||||
override NCWin inputWindow()
|
||||
{
|
||||
return _window.handle();
|
||||
}
|
||||
|
||||
override ScreenAction onShow(ScreenContext context)
|
||||
{
|
||||
return ScreenAction.none();
|
||||
}
|
||||
|
||||
override ScreenAction onChildResult(ScreenContext context, ScreenResult child)
|
||||
{
|
||||
return ScreenAction.none();
|
||||
}
|
||||
|
||||
override ScreenAction handle(ScreenContext context, KeyEvent event)
|
||||
{
|
||||
return ScreenAction.none();
|
||||
}
|
||||
|
||||
override void close()
|
||||
{
|
||||
if (_window !is null)
|
||||
_window.close();
|
||||
_window = null;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue