Compare commits
1 commit
| Author | SHA1 | Date | |
|---|---|---|---|
| 79032d696e |
7 changed files with 280 additions and 11 deletions
|
|
@ -7,5 +7,8 @@
|
|||
"ncui": {
|
||||
"path": ".."
|
||||
}
|
||||
}
|
||||
},
|
||||
"libs": [
|
||||
"form"
|
||||
]
|
||||
}
|
||||
|
|
|
|||
|
|
@ -22,7 +22,12 @@ final class Simple : ScreenBase
|
|||
|
||||
override void build(ScreenContext context, Window window, WidgetContainer ui)
|
||||
{
|
||||
auto okBtn = new Button(3, 2, "OK", () => ScreenAction.push(new Simple()));
|
||||
auto textIntput = new TextInput(5, 2, 10, "Hello");
|
||||
// auto okBtn = new Button(3, 2, "OK", () => ScreenAction.push(new Simple()));
|
||||
auto okBtn = new Button(3, 2, "OK", () {
|
||||
textIntput.close();
|
||||
return ScreenAction.quit(ScreenResult.none());
|
||||
});
|
||||
auto cancelBtn = new Button(3, 9, "Cancel", () => ScreenAction.pop(ScreenResult.none()));
|
||||
|
||||
auto disableOk = new Checkbox(4, 2,"Disable OK", false, (checked) {
|
||||
|
|
@ -32,6 +37,7 @@ final class Simple : ScreenBase
|
|||
_ui.add(okBtn);
|
||||
_ui.add(cancelBtn);
|
||||
_ui.add(disableOk);
|
||||
_ui.add(textIntput);
|
||||
}
|
||||
|
||||
override ScreenAction handleGlobal(ScreenContext context, KeyEvent event)
|
||||
|
|
|
|||
|
|
@ -22,4 +22,14 @@ struct NCWin
|
|||
{
|
||||
return _p is null;
|
||||
}
|
||||
|
||||
void opAssign(NCWin rhs)
|
||||
{
|
||||
_p = rhs._p;
|
||||
}
|
||||
|
||||
void opAssign(WINDOW* rhs)
|
||||
{
|
||||
_p = rhs;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -131,12 +131,13 @@ final class Session
|
|||
private:
|
||||
NCWin _root;
|
||||
bool _ended;
|
||||
SessionConfig _config;
|
||||
|
||||
// Применяет параметры конфигурации к активной ncurses-сессии.
|
||||
void setup(ref const(SessionConfig) config)
|
||||
void setup(ref const(SessionConfig) sc)
|
||||
{
|
||||
// Настройка режима обработки ввода терминалом.
|
||||
final switch (config.mode)
|
||||
final switch (sc.mode)
|
||||
{
|
||||
case InputMode.raw:
|
||||
ncuiNotErr!nocbreak();
|
||||
|
|
@ -154,7 +155,7 @@ private:
|
|||
break;
|
||||
}
|
||||
// Настройка отображения вводимых символов.
|
||||
final switch (config.echo)
|
||||
final switch (sc.echo)
|
||||
{
|
||||
case Echo.on:
|
||||
ncuiNotErr!echo();
|
||||
|
|
@ -165,15 +166,15 @@ private:
|
|||
break;
|
||||
}
|
||||
// Настройка видимости курсора.
|
||||
ncuiNotErr!curs_set(config.cursor);
|
||||
ncuiNotErr!curs_set(sc.cursor);
|
||||
// Настройка задержки при нажатии на ESC
|
||||
ncuiNotErr!set_escdelay(config.escDelay);
|
||||
ncuiNotErr!set_escdelay(sc.escDelay);
|
||||
// Настройка обработки специальных клавиш
|
||||
ncuiNotErr!keypad(_root, config.keypad);
|
||||
ncuiNotErr!keypad(_root, sc.keypad);
|
||||
}
|
||||
|
||||
public:
|
||||
this(const SessionConfig config)
|
||||
this(const SessionConfig sc)
|
||||
{
|
||||
// Если на этапе инициализации сработает проблема с конфигурированием сессии
|
||||
scope (failure)
|
||||
|
|
@ -193,7 +194,9 @@ public:
|
|||
// Установить флаг инициализации ncurses
|
||||
gInitialized = true;
|
||||
// Применение конфигурации
|
||||
setup(config);
|
||||
setup(sc);
|
||||
|
||||
_config = sc;
|
||||
}
|
||||
|
||||
NCWin root()
|
||||
|
|
@ -201,6 +204,11 @@ public:
|
|||
return _root;
|
||||
}
|
||||
|
||||
@property SessionConfig config()
|
||||
{
|
||||
return _config;
|
||||
}
|
||||
|
||||
KeyEvent readKey(NCWin inputWindow)
|
||||
{
|
||||
dchar ch;
|
||||
|
|
|
|||
|
|
@ -14,6 +14,8 @@ protected:
|
|||
WidgetContainer _ui;
|
||||
bool _built;
|
||||
|
||||
bool _clearNext = true;
|
||||
|
||||
void ensureWindow(ScreenContext context);
|
||||
void build(ScreenContext context, Window window, WidgetContainer ui);
|
||||
void layout(ScreenContext context, Window window, WidgetContainer ui)
|
||||
|
|
@ -31,7 +33,12 @@ private:
|
|||
import deimos.ncurses : doupdate;
|
||||
import ncui.lib.checks;
|
||||
|
||||
if (_clearNext)
|
||||
{
|
||||
_window.erase();
|
||||
_clearNext = false;
|
||||
}
|
||||
|
||||
layout(context, _window, _ui);
|
||||
_ui.render(_window, context);
|
||||
_window.noutrefresh();
|
||||
|
|
@ -65,6 +72,8 @@ public:
|
|||
_built = true;
|
||||
}
|
||||
|
||||
_clearNext = true;
|
||||
|
||||
renderAll(context);
|
||||
return ScreenAction.none();
|
||||
}
|
||||
|
|
@ -103,6 +112,7 @@ public:
|
|||
|
||||
_window = null;
|
||||
_built = false;
|
||||
_clearNext = true;
|
||||
_ui = new WidgetContainer();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -19,3 +19,4 @@ public import ncui.lib.logger;
|
|||
public import ncui.widgets.container;
|
||||
public import ncui.widgets.button;
|
||||
public import ncui.widgets.checkbox;
|
||||
public import ncui.widgets.textinput;
|
||||
|
|
|
|||
231
source/ncui/widgets/textinput.d
Normal file
231
source/ncui/widgets/textinput.d
Normal file
|
|
@ -0,0 +1,231 @@
|
|||
module ncui.widgets.textinput;
|
||||
|
||||
import deimos.form;
|
||||
|
||||
import ncui.widgets.widget;
|
||||
import ncui.core.window;
|
||||
import ncui.core.event;
|
||||
import ncui.core.ncwin;
|
||||
import ncui.engine.screen;
|
||||
import ncui.engine.action;
|
||||
import ncui.lib.checks;
|
||||
|
||||
import std.string : toStringz;
|
||||
|
||||
alias OnChange = void delegate(string text);
|
||||
|
||||
final class TextInput : IWidget
|
||||
{
|
||||
private:
|
||||
OnChange _onChange;
|
||||
int _y;
|
||||
int _x;
|
||||
int _width;
|
||||
bool _enabled = true;
|
||||
|
||||
FIELD* _field;
|
||||
// 0: field, 1: null (терминатор для new_form)
|
||||
FIELD*[2] _fields;
|
||||
FORM* _form;
|
||||
|
||||
NCWin _window;
|
||||
NCWin _subWindow;
|
||||
|
||||
bool _posted = false;
|
||||
|
||||
void ensureCreated()
|
||||
{
|
||||
if (_form !is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_field = new_field(1, _width, 0, 0, 0, 0);
|
||||
|
||||
set_field_back(_field, A_UNDERLINE);
|
||||
field_opts_off(_field, O_AUTOSKIP);
|
||||
field_opts_on(_field, O_EDIT);
|
||||
|
||||
_fields[0] = _field;
|
||||
_fields[1] = null;
|
||||
|
||||
_form = new_form(cast(FIELD**) _fields.ptr);
|
||||
}
|
||||
|
||||
void bindTo(Window window)
|
||||
{
|
||||
auto parent = window.handle;
|
||||
|
||||
if (_window.ptr == parent.ptr && !_subWindow.isNull)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (_posted)
|
||||
{
|
||||
unpost_form(_form);
|
||||
_posted = false;
|
||||
}
|
||||
|
||||
if (!_subWindow.isNull)
|
||||
{
|
||||
delwin(_subWindow);
|
||||
_subWindow = NCWin(null);
|
||||
}
|
||||
|
||||
_subWindow = derwin(parent, 1, _width, _y, _x);
|
||||
_window = parent;
|
||||
|
||||
set_form_win(_form, parent);
|
||||
set_form_sub(_form, _subWindow);
|
||||
|
||||
post_form(_form);
|
||||
_posted = true;
|
||||
}
|
||||
|
||||
void applyStyle(ScreenContext context, bool focused)
|
||||
{
|
||||
if (focused && _enabled)
|
||||
{
|
||||
set_field_back(_field, A_UNDERLINE | A_REVERSE);
|
||||
pos_form_cursor(_form);
|
||||
|
||||
import ncui.core.session : Cursor;
|
||||
|
||||
curs_set(Cursor.high);
|
||||
}
|
||||
else
|
||||
{
|
||||
set_field_back(_field, A_UNDERLINE);
|
||||
curs_set(context.session.config.cursor);
|
||||
}
|
||||
}
|
||||
|
||||
void driveRequest(int request)
|
||||
{
|
||||
form_driver_w(_form, KEY_CODE_YES, request);
|
||||
}
|
||||
|
||||
void driveChar(dchar ch)
|
||||
{
|
||||
form_driver_w(_form, OK, ch);
|
||||
}
|
||||
|
||||
public:
|
||||
this(int y, int x, int width, string initial = string.init, OnChange onChange = null)
|
||||
{
|
||||
_y = y;
|
||||
_x = x;
|
||||
_width = width;
|
||||
_onChange = onChange;
|
||||
|
||||
ensureCreated();
|
||||
|
||||
if (initial.length)
|
||||
{
|
||||
set_field_buffer(_field, 0, initial.toStringz);
|
||||
}
|
||||
}
|
||||
|
||||
override @property bool focusable()
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
override @property bool enabled()
|
||||
{
|
||||
return _enabled;
|
||||
}
|
||||
|
||||
override void render(Window window, ScreenContext context, bool focused)
|
||||
{
|
||||
ensureCreated();
|
||||
bindTo(window);
|
||||
applyStyle(context, focused);
|
||||
}
|
||||
|
||||
override ScreenAction handle(ScreenContext context, KeyEvent event)
|
||||
{
|
||||
if (!_enabled)
|
||||
{
|
||||
return ScreenAction.none();
|
||||
}
|
||||
|
||||
bool changed = false;
|
||||
|
||||
if (event.isKeyCode)
|
||||
{
|
||||
switch (event.ch)
|
||||
{
|
||||
case KEY_LEFT:
|
||||
driveRequest(REQ_PREV_CHAR);
|
||||
break;
|
||||
case KEY_RIGHT:
|
||||
driveRequest(REQ_NEXT_CHAR);
|
||||
break;
|
||||
case KEY_HOME:
|
||||
driveRequest(REQ_BEG_LINE);
|
||||
break;
|
||||
case KEY_END:
|
||||
driveRequest(REQ_END_LINE);
|
||||
break;
|
||||
case KEY_BACKSPACE:
|
||||
driveRequest(REQ_DEL_PREV);
|
||||
changed = true;
|
||||
break;
|
||||
case KEY_DC:
|
||||
driveRequest(REQ_DEL_CHAR);
|
||||
changed = true;
|
||||
break;
|
||||
default:
|
||||
return ScreenAction.none();
|
||||
}
|
||||
}
|
||||
else if (event.isChar)
|
||||
{
|
||||
// Backspace иногда приходит как символ (127 или '\b')
|
||||
if (event.ch == 127 || event.ch == '\b')
|
||||
{
|
||||
driveRequest(REQ_DEL_PREV);
|
||||
changed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
driveChar(event.ch);
|
||||
changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
return ScreenAction.none();
|
||||
}
|
||||
|
||||
void close()
|
||||
{
|
||||
if (_posted && _form !is null)
|
||||
{
|
||||
unpost_form(_form);
|
||||
_posted = false;
|
||||
}
|
||||
if (_form !is null)
|
||||
{
|
||||
free_form(_form);
|
||||
_form = null;
|
||||
}
|
||||
if (_field !is null)
|
||||
{
|
||||
free_field(_field);
|
||||
_field = null;
|
||||
}
|
||||
if (!_subWindow.isNull)
|
||||
{
|
||||
delwin(_subWindow);
|
||||
_subWindow = NCWin(null);
|
||||
}
|
||||
_window = NCWin(null);
|
||||
}
|
||||
|
||||
~this()
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue